From jython-checkins at python.org Wed Nov 2 00:26:36 2011 From: jython-checkins at python.org (philip.jenvey) Date: Wed, 02 Nov 2011 00:26:36 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=282=2E5=29=3A_closed_the_mista?= =?utf8?q?ken_duplicate_of_the_2=2E5_branch?= Message-ID: http://hg.python.org/jython/rev/ba82edbd3598 changeset: 6262:ba82edbd3598 branch: 2.5 parent: 6260:936bd1b132eb user: Philip Jenvey date: Tue Nov 01 16:06:16 2011 -0700 summary: closed the mistaken duplicate of the 2.5 branch files: -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed Nov 2 00:26:36 2011 From: jython-checkins at python.org (philip.jenvey) Date: Wed, 02 Nov 2011 00:26:36 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=282=2E5=29=3A_Fix_for_some_min?= =?utf8?q?or_socket_issues_relating_to_attributes=2E?= Message-ID: http://hg.python.org/jython/rev/e93cb387ae49 changeset: 6263:e93cb387ae49 branch: 2.5 parent: 6256:700d0f7c35db user: Alan Kennedy date: Sat Oct 29 18:49:09 2011 +0100 summary: Fix for some minor socket issues relating to attributes. http://bugs.jython.org/issue1803 http://bugs.jython.org/issue1804 (transplanted from be3145efdbb1236f94d6227187312cf00e43b963) files: Lib/socket.py | 18 +++++++++++++----- Lib/test/test_socket.py | 17 ++++++++++++----- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/Lib/socket.py b/Lib/socket.py --- a/Lib/socket.py +++ b/Lib/socket.py @@ -581,17 +581,25 @@ return None return Protocol.getProtocolByName(protocol_name).getProto() -def _realsocket(family = AF_INET, type = SOCK_STREAM, protocol=0): +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" - assert type in (SOCK_DGRAM, SOCK_STREAM), "Only SOCK_STREAM and SOCK_DGRAM sockets are currently supported on jython" - if type == SOCK_STREAM: + assert sock_type in (SOCK_DGRAM, SOCK_STREAM), "Only SOCK_STREAM and SOCK_DGRAM sockets are currently supported on jython" + if sock_type == SOCK_STREAM: if protocol != 0: assert protocol == IPPROTO_TCP, "Only IPPROTO_TCP supported on SOCK_STREAM sockets" - return _tcpsocket() + else: + protocol = IPPROTO_TCP + result = _tcpsocket() else: if protocol != 0: assert protocol == IPPROTO_UDP, "Only IPPROTO_UDP supported on SOCK_DGRAM sockets" - return _udpsocket() + else: + protocol = IPPROTO_UDP + result = _udpsocket() + setattr(result, "family", family) + setattr(result, "type", sock_type) + setattr(result, "proto", protocol) + return result # # Attempt to provide IDNA (RFC 3490) support. 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 @@ -27,11 +27,6 @@ is_bsd = os_name == 'Mac OS X' or 'BSD' in os_name is_solaris = os_name == 'SunOS' -try: - True -except NameError: - True, False = 1, 0 - class SocketTCPTest(unittest.TestCase): HOST = HOST @@ -491,6 +486,18 @@ name = sock.getsockname() self.assertEqual(name, ("0.0.0.0", PORT+1)) + def testSockAttributes(self): + # Testing required attributes + for family in [socket.AF_INET, socket.AF_INET6]: + for sock_type in [socket.SOCK_STREAM, socket.SOCK_DGRAM]: + s = socket.socket(family, sock_type) + self.assertEqual(s.family, family) + self.assertEqual(s.type, sock_type) + if sock_type == socket.SOCK_STREAM: + self.assertEqual(s.proto, socket.IPPROTO_TCP) + else: + self.assertEqual(s.proto, socket.IPPROTO_UDP) + def testGetSockOpt(self): # Testing getsockopt() # We know a socket should start without reuse==0 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed Nov 2 00:26:36 2011 From: jython-checkins at python.org (philip.jenvey) Date: Wed, 02 Nov 2011 00:26:36 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=282=2E5=29=3A_Fix_for_xml_attr?= =?utf8?q?ibute_namespaces_issue?= Message-ID: http://hg.python.org/jython/rev/0ba48ac4ed54 changeset: 6264:0ba48ac4ed54 branch: 2.5 user: Alan Kennedy date: Sun Oct 30 13:07:20 2011 +0000 summary: Fix for xml attribute namespaces issue http://bugs.jython.org/issue1768 (transplanted from 936bd1b132eb9c591cf915b060c6567ae8e16914) files: Lib/test/test_sax.py | 46 ++++++++-------- Lib/xml/sax/drivers2/drv_javasax.py | 2 +- NEWS | 3 + 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/Lib/test/test_sax.py b/Lib/test/test_sax.py --- a/Lib/test/test_sax.py +++ b/Lib/test/test_sax.py @@ -390,22 +390,23 @@ gather = AttrGatherer() parser.setContentHandler(gather) - parser.parse(StringIO("" % ns_uri)) + a_name = "id" ; a_val = "val" + parser.parse(StringIO("" % (ns_uri, a_name, a_val) )) attrs = gather._attrs return attrs.getLength() == 1 and \ - attrs.getNames() == [(ns_uri, "attr")] and \ - attrs.getQNames() == ["ns:attr"] and \ + attrs.getNames() == [(ns_uri, a_name)] and \ + attrs.getQNames() == ["ns:%s" % a_name] and \ len(attrs) == 1 and \ - attrs.has_key((ns_uri, "attr")) and \ - attrs.keys() == [(ns_uri, "attr")] and \ - attrs.get((ns_uri, "attr")) == "val" and \ - attrs.get((ns_uri, "attr"), 25) == "val" and \ - attrs.items() == [((ns_uri, "attr"), "val")] and \ - attrs.values() == ["val"] and \ - attrs.getValue((ns_uri, "attr")) == "val" and \ - attrs[(ns_uri, "attr")] == "val" + attrs.has_key((ns_uri, a_name)) and \ + attrs.keys() == [(ns_uri, a_name)] and \ + attrs.get((ns_uri, a_name)) == a_val and \ + attrs.get((ns_uri, a_name), 25) == a_val and \ + attrs.items() == [((ns_uri, a_name), a_val)] and \ + attrs.values() == [a_val] and \ + attrs.getValue((ns_uri, a_name)) == a_val and \ + attrs[(ns_uri, a_name)] == a_val def test_expat_nsattrs_no_namespace(): parser = make_parser() @@ -413,22 +414,23 @@ gather = AttrGatherer() parser.setContentHandler(gather) - parser.parse(StringIO("")) + a_name = "id" ; a_val = "val" + parser.parse(StringIO("" % (a_name, a_val) )) attrs = gather._attrs return attrs.getLength() == 1 and \ - attrs.getNames() == [(None, "attr")] and \ - attrs.getQNames() == ["attr"] and \ + attrs.getNames() == [(None, a_name)] and \ + attrs.getQNames() == [a_name] and \ len(attrs) == 1 and \ - attrs.has_key((None, "attr")) and \ - attrs.keys() == [(None, "attr")] and \ - attrs.get((None, "attr")) == "val" and \ - attrs.get((None, "attr"), 25) == "val" and \ - attrs.items() == [((None, "attr"), "val")] and \ - attrs.values() == ["val"] and \ - attrs.getValue((None, "attr")) == "val" and \ - attrs[(None, "attr")] == "val" + attrs.has_key((None, a_name)) and \ + attrs.keys() == [(None, a_name)] and \ + attrs.get((None, a_name)) == a_val and \ + attrs.get((None, a_name), 25) == a_val and \ + attrs.items() == [((None, a_name), a_val)] and \ + attrs.values() == [a_val] and \ + attrs.getValue((None, a_name)) == a_val and \ + attrs[(None, a_name)] == a_val # ===== InputSource support diff --git a/Lib/xml/sax/drivers2/drv_javasax.py b/Lib/xml/sax/drivers2/drv_javasax.py --- a/Lib/xml/sax/drivers2/drv_javasax.py +++ b/Lib/xml/sax/drivers2/drv_javasax.py @@ -238,7 +238,7 @@ pass # TODO def _fixTuple(nsTuple, frm, to): - if len(nsTuple) == 2: + if isinstance(nsTuple, tuple) and len(nsTuple) == 2: nsUri, localName = nsTuple if nsUri == frm: nsUri = to diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -5,6 +5,9 @@ - [ 1727 ] Error in Jython 2.5.2 with os.stat and varargs - [ 1735 ] return type of os.read is unicode, not str - [ 1755 ] os.utime('/tmp/nonexistent-file', None) fails to raise OSError + - [ 1768 ] sax.parse doesn't handle attributes with name 'id' correctly + - [ 1803 ] _tcpsocket doesn't have 'family' attribute + - [ 1804 ] _tcpsocket doesn't have 'type' and 'proto' attributes - [ 1811 ] Recursive import bug w/ SQLAlchemy 0.7.3 Jython 2.5.2 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed Nov 2 00:26:36 2011 From: jython-checkins at python.org (philip.jenvey) Date: Wed, 02 Nov 2011 00:26:36 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf8?q?=3A_merge_with_2=2E5?= Message-ID: http://hg.python.org/jython/rev/1651fbbecffd changeset: 6265:1651fbbecffd parent: 6261:179cb4437158 parent: 6264:0ba48ac4ed54 user: Philip Jenvey date: Tue Nov 01 16:15:59 2011 -0700 summary: merge with 2.5 files: -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed Nov 2 01:32:32 2011 From: jython-checkins at python.org (alan.kennedy) Date: Wed, 02 Nov 2011 01:32:32 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=282=2E5=29=3A_Fixing_the_getad?= =?utf8?q?drinfo_API_to_be_closer_to_the_C_API=2E?= Message-ID: http://hg.python.org/jython/rev/7306b9651a3a changeset: 6266:7306b9651a3a branch: 2.5 parent: 6264:0ba48ac4ed54 user: Alan Kennedy date: Wed Nov 02 00:27:42 2011 +0000 summary: Fixing the getaddrinfo API to be closer to the C API. Fixes http://bugs.jython.org/issue1809 files: Lib/socket.py | 71 ++++++++++++++++---- Lib/test/test_socket.py | 95 ++++++++++++++++++++++++++++- 2 files changed, 149 insertions(+), 17 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 = [] @@ -1514,6 +1552,7 @@ raise StopIteration return line +_GLOBAL_DEFAULT_TIMEOUT = object() # Define the SSL support 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" @@ -1863,7 +1956,7 @@ if False: tests.append(UDPBroadcastTest) suites = [unittest.makeSuite(klass, 'test') for klass in tests] - test_support.run_suite(unittest.TestSuite(suites)) + test_support._run_suite(unittest.TestSuite(suites)) if __name__ == "__main__": test_main() -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed Nov 2 01:32:32 2011 From: jython-checkins at python.org (alan.kennedy) Date: Wed, 02 Nov 2011 01:32:32 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf8?q?=3A_merge_with_2=2E5=3A_Fixing_the_getaddrinfo_API_to_be_closer_?= =?utf8?q?to_the_C_API=2E?= Message-ID: http://hg.python.org/jython/rev/95a08a9556f0 changeset: 6267:95a08a9556f0 parent: 6265:1651fbbecffd parent: 6266:7306b9651a3a user: Alan Kennedy 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 From jython-checkins at python.org Wed Nov 2 01:50:09 2011 From: jython-checkins at python.org (alan.kennedy) Date: Wed, 02 Nov 2011 01:50:09 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=282=2E5=29=3A_Updating_NEWS_fi?= =?utf8?q?le_with_a_bug_fix_notice=2E?= Message-ID: http://hg.python.org/jython/rev/826ec962e0c1 changeset: 6268:826ec962e0c1 branch: 2.5 parent: 6266:7306b9651a3a user: Alan Kennedy date: Wed Nov 02 00:45:16 2011 +0000 summary: Updating NEWS file with a bug fix notice. files: NEWS | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ - [ 1768 ] sax.parse doesn't handle attributes with name 'id' correctly - [ 1803 ] _tcpsocket doesn't have 'family' attribute - [ 1804 ] _tcpsocket doesn't have 'type' and 'proto' attributes + - [ 1809 ] socket.getaddrinfo sometimes returns an object that crashes in __str__ - [ 1811 ] Recursive import bug w/ SQLAlchemy 0.7.3 Jython 2.5.2 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed Nov 2 01:50:09 2011 From: jython-checkins at python.org (alan.kennedy) Date: Wed, 02 Nov 2011 01:50:09 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf8?q?=3A_merge_with_2=2E5=3A_Updating_NEWS_file_with_a_bug_fix_notice?= =?utf8?q?=2E?= Message-ID: http://hg.python.org/jython/rev/386860c0254c changeset: 6269:386860c0254c parent: 6267:95a08a9556f0 parent: 6268:826ec962e0c1 user: Alan Kennedy date: Wed Nov 02 00:48:43 2011 +0000 summary: merge with 2.5: Updating NEWS file with a bug fix notice. files: NEWS | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ - [ 1768 ] sax.parse doesn't handle attributes with name 'id' correctly - [ 1803 ] _tcpsocket doesn't have 'family' attribute - [ 1804 ] _tcpsocket doesn't have 'type' and 'proto' attributes + - [ 1809 ] socket.getaddrinfo sometimes returns an object that crashes in __str__ - [ 1811 ] Recursive import bug w/ SQLAlchemy 0.7.3 Jython 2.5.2 -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed Nov 2 10:47:50 2011 From: jython-checkins at python.org (alan.kennedy) Date: Wed, 02 Nov 2011 10:47:50 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=282=2E5=29=3A_Introducing_sock?= =?utf8?q?et=2Ecreate=5Fconnection=2E?= Message-ID: http://hg.python.org/jython/rev/cc548bacc001 changeset: 6270:cc548bacc001 branch: 2.5 parent: 6268:826ec962e0c1 user: Alan Kennedy date: Wed Nov 02 09:43:30 2011 +0000 summary: Introducing socket.create_connection. This is required for 2.6, but is also a useful convenience method for 2.5 users. Fixes http://bugs.jython.org/issue1788 files: Lib/socket.py | 29 +++++++++++++++++++++++++++++ 1 files changed, 29 insertions(+), 0 deletions(-) diff --git a/Lib/socket.py b/Lib/socket.py --- a/Lib/socket.py +++ b/Lib/socket.py @@ -1554,6 +1554,35 @@ _GLOBAL_DEFAULT_TIMEOUT = object() +def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT): + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`getdefaulttimeout` + is used. + """ + + msg = "getaddrinfo returns an empty list" + host, port = address + for res in getaddrinfo(host, port, 0, SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket(af, socktype, proto) + if timeout is not _GLOBAL_DEFAULT_TIMEOUT: + sock.settimeout(timeout) + sock.connect(sa) + return sock + + except error, msg: + if sock is not None: + sock.close() + + raise error, msg + # Define the SSL support class ssl: -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed Nov 2 10:47:50 2011 From: jython-checkins at python.org (alan.kennedy) Date: Wed, 02 Nov 2011 10:47:50 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf8?q?=3A_merge_with_2=2E5=3A_Introducing_socket=2Ecreate=5Fconnection?= =?utf8?q?=2E?= Message-ID: http://hg.python.org/jython/rev/47134672d506 changeset: 6271:47134672d506 parent: 6269:386860c0254c parent: 6270:cc548bacc001 user: Alan Kennedy date: Wed Nov 02 09:47:06 2011 +0000 summary: merge with 2.5: Introducing socket.create_connection. files: Lib/socket.py | 29 +++++++++++++++++++++++++++++ 1 files changed, 29 insertions(+), 0 deletions(-) diff --git a/Lib/socket.py b/Lib/socket.py --- a/Lib/socket.py +++ b/Lib/socket.py @@ -1554,6 +1554,35 @@ _GLOBAL_DEFAULT_TIMEOUT = object() +def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT): + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`getdefaulttimeout` + is used. + """ + + msg = "getaddrinfo returns an empty list" + host, port = address + for res in getaddrinfo(host, port, 0, SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket(af, socktype, proto) + if timeout is not _GLOBAL_DEFAULT_TIMEOUT: + sock.settimeout(timeout) + sock.connect(sa) + return sock + + except error, msg: + if sock is not None: + sock.close() + + raise error, msg + # Define the SSL support class ssl: -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Nov 7 02:55:55 2011 From: jython-checkins at python.org (philip.jenvey) Date: Mon, 07 Nov 2011 02:55:55 +0100 Subject: [Jython-checkins] =?utf8?b?anl0aG9uOiBhZGQgX19idWlsdGluX18ubmV4?= =?utf8?q?t?= Message-ID: http://hg.python.org/jython/rev/91b69979ac44 changeset: 6273:91b69979ac44 user: Philip Jenvey date: Sun Nov 06 17:54:18 2011 -0800 summary: add __builtin__.next files: src/org/python/core/__builtin__.java | 32 ++++++++++++++++ 1 files changed, 32 insertions(+), 0 deletions(-) diff --git a/src/org/python/core/__builtin__.java b/src/org/python/core/__builtin__.java --- a/src/org/python/core/__builtin__.java +++ b/src/org/python/core/__builtin__.java @@ -361,6 +361,7 @@ dict.__setitem__("any", new AnyFunction()); dict.__setitem__("format", new FormatFunction()); dict.__setitem__("print", new PrintFunction()); + dict.__setitem__("next", new NextFunction()); } public static PyObject abs(PyObject o) { @@ -1662,3 +1663,34 @@ return PyFile.TYPE.__call__(args, kwds); } } + +class NextFunction extends PyBuiltinFunction { + NextFunction() { + super("next", "next(iterator[, default])\n\n" + + "Return the next item from the iterator. If default is given and the iterator\n" + + "is exhausted, it is returned instead of raising StopIteration."); + } + + @Override + public PyObject __call__(PyObject args[], String kwds[]) { + ArgParser ap = new ArgParser("next", args, kwds, new String[] {"iterator", "default"}, 1); + ap.noKeywords(); + PyObject it = ap.getPyObject(0); + PyObject def = ap.getPyObject(1, null); + + PyObject next; + if ((next = it.__findattr__("next")) == null) { + throw Py.TypeError(String.format("'%.200s' object is not an iterator", + it.getType().fastGetName())); + } + + try { + return next.__call__(); + } catch (PyException pye) { + if (!pye.match(Py.StopIteration) || def == null) { + throw pye; + } + } + return def; + } +} -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Nov 7 02:55:55 2011 From: jython-checkins at python.org (philip.jenvey) Date: Mon, 07 Nov 2011 02:55:55 +0100 Subject: [Jython-checkins] =?utf8?q?jython=3A_fix_test=5Fbuiltin=2Etest=5F?= =?utf8?q?neg?= Message-ID: http://hg.python.org/jython/rev/975a2336fcb3 changeset: 6274:975a2336fcb3 user: Philip Jenvey date: Sun Nov 06 17:55:06 2011 -0800 summary: fix test_builtin.test_neg files: src/org/python/core/PyInteger.java | 9 +++++++-- 1 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/org/python/core/PyInteger.java b/src/org/python/core/PyInteger.java --- a/src/org/python/core/PyInteger.java +++ b/src/org/python/core/PyInteger.java @@ -793,8 +793,13 @@ @ExposedMethod(doc = BuiltinDocs.int___neg___doc) final PyObject int___neg__() { - long x = -getValue(); - return Py.newInteger(x); + long x = getValue(); + long result = -x; + // check for overflow + if (x < 0 && result == x) { + return new PyLong(x).__neg__(); + } + return Py.newInteger(result); } @Override -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Mon Nov 7 02:55:55 2011 From: jython-checkins at python.org (philip.jenvey) Date: Mon, 07 Nov 2011 02:55:55 +0100 Subject: [Jython-checkins] =?utf8?q?jython=3A_upgrade_test=5Fbuiltin=2Epy?= =?utf8?q?=2C_from=3A?= Message-ID: http://hg.python.org/jython/rev/c160531fada7 changeset: 6272:c160531fada7 user: Philip Jenvey date: Sun Nov 06 17:50:15 2011 -0800 summary: upgrade test_builtin.py, from: https://svn.python.org/projects/python/branches/release26-maint/Lib/test/test_builtin.py at 83628 files: Lib/test/test_builtin.py | 867 ++++++++++---------------- 1 files changed, 348 insertions(+), 519 deletions(-) diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -1,18 +1,17 @@ # Python test set -- built-in functions -import test.test_support, unittest -from test.test_support import fcmp, have_unicode, TESTFN, unlink, \ - run_unittest, is_jython +import platform +import unittest +import warnings +from test.test_support import (fcmp, have_unicode, TESTFN, unlink, + run_unittest, _check_py3k_warnings, check_warnings, + is_jython) from operator import neg -import sys, warnings, cStringIO, random, UserDict -warnings.filterwarnings("ignore", "hex../oct.. of negative int", - FutureWarning, __name__) -warnings.filterwarnings("ignore", "integer argument expected", - DeprecationWarning, "unittest") - +import sys, cStringIO, random, UserDict # count the number of test runs. # used to skip running test_execfile() multiple times +# and to create unique strings to intern in test_intern() numruns = 0 class Squares: @@ -53,45 +52,6 @@ def write(self, line): pass -L = [ - ('0', 0), - ('1', 1), - ('9', 9), - ('10', 10), - ('99', 99), - ('100', 100), - ('314', 314), - (' 314', 314), - ('314 ', 314), - (' \t\t 314 \t\t ', 314), - (repr(sys.maxint), sys.maxint), - (' 1x', ValueError), - (' 1 ', 1), - (' 1\02 ', ValueError), - ('', ValueError), - (' ', ValueError), - (' \t\t ', ValueError) -] -if have_unicode: - L += [ - (unicode('0'), 0), - (unicode('1'), 1), - (unicode('9'), 9), - (unicode('10'), 10), - (unicode('99'), 99), - (unicode('100'), 100), - (unicode('314'), 314), - (unicode(' 314'), 314), - (unicode('\u0663\u0661\u0664 ','raw-unicode-escape'), 314), - (unicode(' \t\t 314 \t\t '), 314), - (unicode(' 1x'), ValueError), - (unicode(' 1 '), 1), - (unicode(' 1\02 '), ValueError), - (unicode(''), ValueError), - (unicode(' '), ValueError), - (unicode(' \t\t '), ValueError), - (unichr(0x200), ValueError), -] class TestFailingBool: def __nonzero__(self): @@ -107,15 +67,19 @@ __import__('sys') __import__('time') __import__('string') + __import__(name='sys') + __import__(name='time', level=0) self.assertRaises(ImportError, __import__, 'spamspam') self.assertRaises(TypeError, __import__, 1, 2, 3, 4) self.assertRaises(ValueError, __import__, '') + self.assertRaises(TypeError, __import__, 'sys', name='sys') def test_abs(self): # int self.assertEqual(abs(0), 0) self.assertEqual(abs(1234), 1234) self.assertEqual(abs(-1234), 1234) + self.assertTrue(abs(-sys.maxint-1) > 0) # float self.assertEqual(abs(0.0), 0.0) self.assertEqual(abs(3.14), 3.14) @@ -155,6 +119,11 @@ S = [10, 20, 30] self.assertEqual(any(x > 42 for x in S), False) + def test_neg(self): + x = -sys.maxint-1 + self.assert_(isinstance(x, int)) + self.assertEqual(-x, sys.maxint+1) + def test_apply(self): def f0(*args): self.assertEqual(args, ()) @@ -237,15 +206,21 @@ compile('print 1\n', '', 'exec') bom = '\xef\xbb\xbf' compile(bom + 'print 1\n', '', 'exec') + compile(source='pass', filename='?', mode='exec') + compile(dont_inherit=0, filename='tmp', source='0', mode='eval') + compile('pass', '?', dont_inherit=1, mode='exec') self.assertRaises(TypeError, compile) self.assertRaises(ValueError, compile, 'print 42\n', '', 'badmode') self.assertRaises(ValueError, compile, 'print 42\n', '', 'single', 0xff) self.assertRaises(TypeError, compile, chr(0), 'f', 'exec') + self.assertRaises(TypeError, compile, 'pass', '?', 'exec', + mode='eval', source='0', filename='tmp') if have_unicode: compile(unicode('print u"\xc3\xa5"\n', 'utf8'), '', 'exec') self.assertRaises(TypeError, compile, unichr(0), 'f', 'exec') self.assertRaises(ValueError, compile, unicode('a = 1'), 'f', 'bad') + def test_delattr(self): import sys sys.spam = 1 @@ -253,11 +228,66 @@ self.assertRaises(TypeError, delattr) def test_dir(self): - x = 1 - self.assert_('x' in dir()) + # dir(wrong number of arguments) + self.assertRaises(TypeError, dir, 42, 42) + + # dir() - local scope + local_var = 1 + self.assert_('local_var' in dir()) + + # dir(module) import sys - self.assert_('modules' in dir(sys)) - self.assertRaises(TypeError, dir, 42, 42) + self.assert_('exit' in dir(sys)) + + # dir(module_with_invalid__dict__) + import types + class Foo(types.ModuleType): + __dict__ = 8 + f = Foo("foo") + self.assertRaises(TypeError, dir, f) + + # dir(type) + self.assert_("strip" in dir(str)) + self.assert_("__mro__" not in dir(str)) + + # dir(obj) + class Foo(object): + def __init__(self): + self.x = 7 + self.y = 8 + self.z = 9 + f = Foo() + self.assert_("y" in dir(f)) + + # dir(obj_no__dict__) + class Foo(object): + __slots__ = [] + f = Foo() + self.assert_("__repr__" in dir(f)) + + # dir(obj_no__class__with__dict__) + # (an ugly trick to cause getattr(f, "__class__") to fail) + class Foo(object): + __slots__ = ["__class__", "__dict__"] + def __init__(self): + self.bar = "wow" + f = Foo() + self.assert_("__repr__" not in dir(f)) + self.assert_("bar" in dir(f)) + + # dir(obj_using __dir__) + class Foo(object): + def __dir__(self): + return ["kan", "ga", "roo"] + f = Foo() + self.assert_(dir(f) == ["ga", "kan", "roo"]) + + # dir(obj__dir__not_list) + class Foo(object): + def __dir__(self): + return 7 + f = Foo() + self.assertRaises(TypeError, dir, f) def test_divmod(self): self.assertEqual(divmod(12, 7), (1, 5)) @@ -388,7 +418,9 @@ f.write('z = z+1\n') f.write('z = z*2\n') f.close() - execfile(TESTFN) + with _check_py3k_warnings(("execfile.. not supported in 3.x", + DeprecationWarning)): + execfile(TESTFN) def test_execfile(self): global numruns @@ -544,88 +576,6 @@ self.assertEqual(outp, exp) self.assert_(not isinstance(outp, cls)) - def test_float(self): - self.assertEqual(float(3.14), 3.14) - self.assertEqual(float(314), 314.0) - self.assertEqual(float(314L), 314.0) - self.assertEqual(float(" 3.14 "), 3.14) - self.assertRaises(ValueError, float, " 0x3.1 ") - #XXX: Java can handle conversions of BinaryExponentIndicator (the p - # below) as well as floating hex. Thanks to Tobias Ivarsson for - # noticing this in the Java Language Spec. For us this is - # cross-platform - and the comments for this checkin by Neal - # Norwitz say this check is part of making things more platform - # neutral. I'm leaving this test out until we find out whether or - # not this is an implementation detail of CPython - if not is_jython: - self.assertRaises(ValueError, float, " -0x3.p-1 ") - if have_unicode: - self.assertEqual(float(unicode(" 3.14 ")), 3.14) - self.assertEqual(float(unicode(" \u0663.\u0661\u0664 ",'raw-unicode-escape')), 3.14) - # Implementation limitation in PyFloat_FromString() - # XXX: but not in Jython. - if is_jython: - self.assertEquals(float(unicode("1"*10000)), float(unicode("1"*10000))) - else: - self.assertRaises(ValueError, float, unicode("1"*10000)) - -# @run_with_locale('LC_NUMERIC', 'fr_FR', 'de_DE') -# def test_float_with_comma(self): -# # set locale to something that doesn't use '.' for the decimal point -# # float must not accept the locale specific decimal point but -# # it still has to accept the normal python syntac -# import locale -# if not locale.localeconv()['decimal_point'] == ',': -# return - -# self.assertEqual(float(" 3.14 "), 3.14) -# self.assertEqual(float("+3.14 "), 3.14) -# self.assertEqual(float("-3.14 "), -3.14) -# self.assertEqual(float(".14 "), .14) -# self.assertEqual(float("3. "), 3.0) -# self.assertEqual(float("3.e3 "), 3000.0) -# self.assertEqual(float("3.2e3 "), 3200.0) -# self.assertEqual(float("2.5e-1 "), 0.25) -# self.assertEqual(float("5e-1"), 0.5) -# self.assertRaises(ValueError, float, " 3,14 ") -# self.assertRaises(ValueError, float, " +3,14 ") -# self.assertRaises(ValueError, float, " -3,14 ") -# self.assertRaises(ValueError, float, " 0x3.1 ") -# self.assertRaises(ValueError, float, " -0x3.p-1 ") -# self.assertEqual(float(" 25.e-1 "), 2.5) -# self.assertEqual(fcmp(float(" .25e-1 "), .025), 0) - - def test_floatconversion(self): - # Make sure that calls to __float__() work properly - class Foo0: - def __float__(self): - return 42. - - class Foo1(object): - def __float__(self): - return 42. - - class Foo2(float): - def __float__(self): - return 42. - - class Foo3(float): - def __new__(cls, value=0.): - return float.__new__(cls, 2*value) - - def __float__(self): - return self - - class Foo4(float): - def __float__(self): - return 42 - - self.assertAlmostEqual(float(Foo0()), 42.) - self.assertAlmostEqual(float(Foo1()), 42.) - self.assertAlmostEqual(float(Foo2()), 42.) - self.assertAlmostEqual(float(Foo3(21)), 42.) - self.assertRaises(TypeError, float, Foo4(42)) - def test_getattr(self): import sys self.assert_(getattr(sys, 'stdout') is sys.stdout) @@ -643,6 +593,16 @@ if have_unicode: self.assertRaises(UnicodeError, hasattr, sys, unichr(sys.maxunicode)) + # Check that hasattr allows SystemExit and KeyboardInterrupts by + class A: + def __getattr__(self, what): + raise KeyboardInterrupt + self.assertRaises(KeyboardInterrupt, hasattr, A(), "b") + class B: + def __getattr__(self, what): + raise SystemExit + self.assertRaises(SystemExit, hasattr, B(), "b") + def test_hash(self): hash(None) self.assertEqual(hash(1), hash(1L)) @@ -687,180 +647,11 @@ # Test input() later, together with raw_input - def test_int(self): - self.assertEqual(int(314), 314) - self.assertEqual(int(3.14), 3) - self.assertEqual(int(314L), 314) - # Check that conversion from float truncates towards zero - self.assertEqual(int(-3.14), -3) - self.assertEqual(int(3.9), 3) - self.assertEqual(int(-3.9), -3) - self.assertEqual(int(3.5), 3) - self.assertEqual(int(-3.5), -3) - # Different base: - self.assertEqual(int("10",16), 16L) - if have_unicode: - self.assertEqual(int(unicode("10"),16), 16L) - # Test conversion from strings and various anomalies - for s, v in L: - for sign in "", "+", "-": - for prefix in "", " ", "\t", " \t\t ": - ss = prefix + sign + s - vv = v - if sign == "-" and v is not ValueError: - vv = -v - try: - self.assertEqual(int(ss), vv) - except v: - pass - - s = repr(-1-sys.maxint) - self.assertEqual(int(s)+1, -sys.maxint) - # should return long - int(s[1:]) - - # should return long - x = int(1e100) - self.assert_(isinstance(x, long)) - x = int(-1e100) - self.assert_(isinstance(x, long)) - - - # SF bug 434186: 0x80000000/2 != 0x80000000>>1. - # Worked by accident in Windows release build, but failed in debug build. - # Failed in all Linux builds. - x = -1-sys.maxint - self.assertEqual(x >> 1, x//2) - - self.assertRaises(ValueError, int, '123\0') - self.assertRaises(ValueError, int, '53', 40) - - x = int('1' * 600) - self.assert_(isinstance(x, long)) - - if have_unicode: - x = int(unichr(0x661) * 600) - self.assert_(isinstance(x, long)) - - self.assertRaises(TypeError, int, 1, 12) - - self.assertEqual(int('0123', 0), 83) - self.assertEqual(int('0x123', 16), 291) - - # SF bug 1334662: int(string, base) wrong answers - # Various representations of 2**32 evaluated to 0 - # rather than 2**32 in previous versions - - self.assertEqual(int('100000000000000000000000000000000', 2), 4294967296L) - self.assertEqual(int('102002022201221111211', 3), 4294967296L) - self.assertEqual(int('10000000000000000', 4), 4294967296L) - self.assertEqual(int('32244002423141', 5), 4294967296L) - self.assertEqual(int('1550104015504', 6), 4294967296L) - self.assertEqual(int('211301422354', 7), 4294967296L) - self.assertEqual(int('40000000000', 8), 4294967296L) - self.assertEqual(int('12068657454', 9), 4294967296L) - self.assertEqual(int('4294967296', 10), 4294967296L) - self.assertEqual(int('1904440554', 11), 4294967296L) - self.assertEqual(int('9ba461594', 12), 4294967296L) - self.assertEqual(int('535a79889', 13), 4294967296L) - self.assertEqual(int('2ca5b7464', 14), 4294967296L) - self.assertEqual(int('1a20dcd81', 15), 4294967296L) - self.assertEqual(int('100000000', 16), 4294967296L) - self.assertEqual(int('a7ffda91', 17), 4294967296L) - self.assertEqual(int('704he7g4', 18), 4294967296L) - self.assertEqual(int('4f5aff66', 19), 4294967296L) - self.assertEqual(int('3723ai4g', 20), 4294967296L) - self.assertEqual(int('281d55i4', 21), 4294967296L) - self.assertEqual(int('1fj8b184', 22), 4294967296L) - self.assertEqual(int('1606k7ic', 23), 4294967296L) - self.assertEqual(int('mb994ag', 24), 4294967296L) - self.assertEqual(int('hek2mgl', 25), 4294967296L) - self.assertEqual(int('dnchbnm', 26), 4294967296L) - self.assertEqual(int('b28jpdm', 27), 4294967296L) - self.assertEqual(int('8pfgih4', 28), 4294967296L) - self.assertEqual(int('76beigg', 29), 4294967296L) - self.assertEqual(int('5qmcpqg', 30), 4294967296L) - self.assertEqual(int('4q0jto4', 31), 4294967296L) - self.assertEqual(int('4000000', 32), 4294967296L) - self.assertEqual(int('3aokq94', 33), 4294967296L) - self.assertEqual(int('2qhxjli', 34), 4294967296L) - self.assertEqual(int('2br45qb', 35), 4294967296L) - self.assertEqual(int('1z141z4', 36), 4294967296L) - - # SF bug 1334662: int(string, base) wrong answers - # Checks for proper evaluation of 2**32 + 1 - self.assertEqual(int('100000000000000000000000000000001', 2), 4294967297L) - self.assertEqual(int('102002022201221111212', 3), 4294967297L) - self.assertEqual(int('10000000000000001', 4), 4294967297L) - self.assertEqual(int('32244002423142', 5), 4294967297L) - self.assertEqual(int('1550104015505', 6), 4294967297L) - self.assertEqual(int('211301422355', 7), 4294967297L) - self.assertEqual(int('40000000001', 8), 4294967297L) - self.assertEqual(int('12068657455', 9), 4294967297L) - self.assertEqual(int('4294967297', 10), 4294967297L) - self.assertEqual(int('1904440555', 11), 4294967297L) - self.assertEqual(int('9ba461595', 12), 4294967297L) - self.assertEqual(int('535a7988a', 13), 4294967297L) - self.assertEqual(int('2ca5b7465', 14), 4294967297L) - self.assertEqual(int('1a20dcd82', 15), 4294967297L) - self.assertEqual(int('100000001', 16), 4294967297L) - self.assertEqual(int('a7ffda92', 17), 4294967297L) - self.assertEqual(int('704he7g5', 18), 4294967297L) - self.assertEqual(int('4f5aff67', 19), 4294967297L) - self.assertEqual(int('3723ai4h', 20), 4294967297L) - self.assertEqual(int('281d55i5', 21), 4294967297L) - self.assertEqual(int('1fj8b185', 22), 4294967297L) - self.assertEqual(int('1606k7id', 23), 4294967297L) - self.assertEqual(int('mb994ah', 24), 4294967297L) - self.assertEqual(int('hek2mgm', 25), 4294967297L) - self.assertEqual(int('dnchbnn', 26), 4294967297L) - self.assertEqual(int('b28jpdn', 27), 4294967297L) - self.assertEqual(int('8pfgih5', 28), 4294967297L) - self.assertEqual(int('76beigh', 29), 4294967297L) - self.assertEqual(int('5qmcpqh', 30), 4294967297L) - self.assertEqual(int('4q0jto5', 31), 4294967297L) - self.assertEqual(int('4000001', 32), 4294967297L) - self.assertEqual(int('3aokq95', 33), 4294967297L) - self.assertEqual(int('2qhxjlj', 34), 4294967297L) - self.assertEqual(int('2br45qc', 35), 4294967297L) - self.assertEqual(int('1z141z5', 36), 4294967297L) - - def test_intconversion(self): - # Test __int__() - class Foo0: - def __int__(self): - return 42 - - class Foo1(object): - def __int__(self): - return 42 - - class Foo2(int): - def __int__(self): - return 42 - - class Foo3(int): - def __int__(self): - return self - - class Foo4(int): - def __int__(self): - return 42L - - class Foo5(int): - def __int__(self): - return 42. - - self.assertEqual(int(Foo0()), 42) - self.assertEqual(int(Foo1()), 42) - self.assertEqual(int(Foo2()), 42) - self.assertEqual(int(Foo3()), 0) - self.assertEqual(int(Foo4()), 42L) - self.assertRaises(TypeError, int, Foo5()) - def test_intern(self): self.assertRaises(TypeError, intern) - s = "never interned before" + # This fails if the test is run twice with a constant string, + # therefore append the run counter + s = "never interned before " + str(numruns) self.assert_(intern(s) is s) s2 = s.swapcase().swapcase() self.assert_(intern(s2) is s) @@ -941,191 +732,6 @@ raise ValueError self.assertRaises(ValueError, len, BadSeq()) - def test_list(self): - self.assertEqual(list([]), []) - l0_3 = [0, 1, 2, 3] - l0_3_bis = list(l0_3) - self.assertEqual(l0_3, l0_3_bis) - self.assert_(l0_3 is not l0_3_bis) - self.assertEqual(list(()), []) - self.assertEqual(list((0, 1, 2, 3)), [0, 1, 2, 3]) - self.assertEqual(list(''), []) - self.assertEqual(list('spam'), ['s', 'p', 'a', 'm']) - - if sys.maxint == 0x7fffffff and not test.test_support.is_jython: - # This test can currently only work on 32-bit machines. - # XXX If/when PySequence_Length() returns a ssize_t, it should be - # XXX re-enabled. - # Verify clearing of bug #556025. - # This assumes that the max data size (sys.maxint) == max - # address size this also assumes that the address size is at - # least 4 bytes with 8 byte addresses, the bug is not well - # tested - # - # Note: This test is expected to SEGV under Cygwin 1.3.12 or - # earlier due to a newlib bug. See the following mailing list - # thread for the details: - - # http://sources.redhat.com/ml/newlib/2002/msg00369.html - self.assertRaises(MemoryError, list, xrange(sys.maxint // 2)) - - # This code used to segfault in Py2.4a3 - x = [] - x.extend(-y for y in x) - self.assertEqual(x, []) - - def test_long(self): - self.assertEqual(long(314), 314L) - self.assertEqual(long(3.14), 3L) - self.assertEqual(long(314L), 314L) - # Check that conversion from float truncates towards zero - self.assertEqual(long(-3.14), -3L) - self.assertEqual(long(3.9), 3L) - self.assertEqual(long(-3.9), -3L) - self.assertEqual(long(3.5), 3L) - self.assertEqual(long(-3.5), -3L) - self.assertEqual(long("-3"), -3L) - if have_unicode: - self.assertEqual(long(unicode("-3")), -3L) - # Different base: - self.assertEqual(long("10",16), 16L) - if have_unicode: - self.assertEqual(long(unicode("10"),16), 16L) - # Check conversions from string (same test set as for int(), and then some) - LL = [ - ('1' + '0'*20, 10L**20), - ('1' + '0'*100, 10L**100) - ] - L2 = L[:] - if have_unicode: - L2 += [ - (unicode('1') + unicode('0')*20, 10L**20), - (unicode('1') + unicode('0')*100, 10L**100), - ] - for s, v in L2 + LL: - for sign in "", "+", "-": - for prefix in "", " ", "\t", " \t\t ": - ss = prefix + sign + s - vv = v - if sign == "-" and v is not ValueError: - vv = -v - try: - self.assertEqual(long(ss), long(vv)) - except v: - pass - - self.assertRaises(ValueError, long, '123\0') - self.assertRaises(ValueError, long, '53', 40) - self.assertRaises(TypeError, long, 1, 12) - - self.assertEqual(long('100000000000000000000000000000000', 2), - 4294967296) - self.assertEqual(long('102002022201221111211', 3), 4294967296) - self.assertEqual(long('10000000000000000', 4), 4294967296) - self.assertEqual(long('32244002423141', 5), 4294967296) - self.assertEqual(long('1550104015504', 6), 4294967296) - self.assertEqual(long('211301422354', 7), 4294967296) - self.assertEqual(long('40000000000', 8), 4294967296) - self.assertEqual(long('12068657454', 9), 4294967296) - self.assertEqual(long('4294967296', 10), 4294967296) - self.assertEqual(long('1904440554', 11), 4294967296) - self.assertEqual(long('9ba461594', 12), 4294967296) - self.assertEqual(long('535a79889', 13), 4294967296) - self.assertEqual(long('2ca5b7464', 14), 4294967296) - self.assertEqual(long('1a20dcd81', 15), 4294967296) - self.assertEqual(long('100000000', 16), 4294967296) - self.assertEqual(long('a7ffda91', 17), 4294967296) - self.assertEqual(long('704he7g4', 18), 4294967296) - self.assertEqual(long('4f5aff66', 19), 4294967296) - self.assertEqual(long('3723ai4g', 20), 4294967296) - self.assertEqual(long('281d55i4', 21), 4294967296) - self.assertEqual(long('1fj8b184', 22), 4294967296) - self.assertEqual(long('1606k7ic', 23), 4294967296) - self.assertEqual(long('mb994ag', 24), 4294967296) - self.assertEqual(long('hek2mgl', 25), 4294967296) - self.assertEqual(long('dnchbnm', 26), 4294967296) - self.assertEqual(long('b28jpdm', 27), 4294967296) - self.assertEqual(long('8pfgih4', 28), 4294967296) - self.assertEqual(long('76beigg', 29), 4294967296) - self.assertEqual(long('5qmcpqg', 30), 4294967296) - self.assertEqual(long('4q0jto4', 31), 4294967296) - self.assertEqual(long('4000000', 32), 4294967296) - self.assertEqual(long('3aokq94', 33), 4294967296) - self.assertEqual(long('2qhxjli', 34), 4294967296) - self.assertEqual(long('2br45qb', 35), 4294967296) - self.assertEqual(long('1z141z4', 36), 4294967296) - - self.assertEqual(long('100000000000000000000000000000001', 2), - 4294967297) - self.assertEqual(long('102002022201221111212', 3), 4294967297) - self.assertEqual(long('10000000000000001', 4), 4294967297) - self.assertEqual(long('32244002423142', 5), 4294967297) - self.assertEqual(long('1550104015505', 6), 4294967297) - self.assertEqual(long('211301422355', 7), 4294967297) - self.assertEqual(long('40000000001', 8), 4294967297) - self.assertEqual(long('12068657455', 9), 4294967297) - self.assertEqual(long('4294967297', 10), 4294967297) - self.assertEqual(long('1904440555', 11), 4294967297) - self.assertEqual(long('9ba461595', 12), 4294967297) - self.assertEqual(long('535a7988a', 13), 4294967297) - self.assertEqual(long('2ca5b7465', 14), 4294967297) - self.assertEqual(long('1a20dcd82', 15), 4294967297) - self.assertEqual(long('100000001', 16), 4294967297) - self.assertEqual(long('a7ffda92', 17), 4294967297) - self.assertEqual(long('704he7g5', 18), 4294967297) - self.assertEqual(long('4f5aff67', 19), 4294967297) - self.assertEqual(long('3723ai4h', 20), 4294967297) - self.assertEqual(long('281d55i5', 21), 4294967297) - self.assertEqual(long('1fj8b185', 22), 4294967297) - self.assertEqual(long('1606k7id', 23), 4294967297) - self.assertEqual(long('mb994ah', 24), 4294967297) - self.assertEqual(long('hek2mgm', 25), 4294967297) - self.assertEqual(long('dnchbnn', 26), 4294967297) - self.assertEqual(long('b28jpdn', 27), 4294967297) - self.assertEqual(long('8pfgih5', 28), 4294967297) - self.assertEqual(long('76beigh', 29), 4294967297) - self.assertEqual(long('5qmcpqh', 30), 4294967297) - self.assertEqual(long('4q0jto5', 31), 4294967297) - self.assertEqual(long('4000001', 32), 4294967297) - self.assertEqual(long('3aokq95', 33), 4294967297) - self.assertEqual(long('2qhxjlj', 34), 4294967297) - self.assertEqual(long('2br45qc', 35), 4294967297) - self.assertEqual(long('1z141z5', 36), 4294967297) - - - def test_longconversion(self): - # Test __long__() - class Foo0: - def __long__(self): - return 42L - - class Foo1(object): - def __long__(self): - return 42L - - class Foo2(long): - def __long__(self): - return 42L - - class Foo3(long): - def __long__(self): - return self - - class Foo4(long): - def __long__(self): - return 42 - - class Foo5(long): - def __long__(self): - return 42. - - self.assertEqual(long(Foo0()), 42L) - self.assertEqual(long(Foo1()), 42L) - self.assertEqual(long(Foo2()), 42L) - self.assertEqual(long(Foo3()), 0) - self.assertEqual(long(Foo4()), 42) - self.assertRaises(TypeError, long, Foo5()) - def test_map(self): self.assertEqual( map(None, 'hello world'), @@ -1280,6 +886,33 @@ self.assertEqual(min(data, key=f), sorted(data, key=f)[0]) + def test_next(self): + it = iter(range(2)) + self.assertEqual(next(it), 0) + self.assertEqual(next(it), 1) + self.assertRaises(StopIteration, next, it) + self.assertRaises(StopIteration, next, it) + self.assertEquals(next(it, 42), 42) + + class Iter(object): + def __iter__(self): + return self + def next(self): + raise StopIteration + + it = iter(Iter()) + self.assertEquals(next(it, 42), 42) + self.assertRaises(StopIteration, next, it) + + def gen(): + yield 1 + return + + it = gen() + self.assertEquals(next(it), 1) + self.assertRaises(StopIteration, next, it) + self.assertEquals(next(it, 42), 42) + def test_oct(self): self.assertEqual(oct(100), '0144') self.assertEqual(oct(100L), '0144L') @@ -1386,6 +1019,7 @@ self.assertRaises(ValueError, pow, 1, 2, 0) self.assertRaises(TypeError, pow, -1L, -2L, 3L) self.assertRaises(ValueError, pow, 1L, 2L, 0L) + # Will return complex in 3.0: self.assertRaises(ValueError, pow, -342.43, 0.234) self.assertRaises(TypeError, pow) @@ -1435,11 +1069,17 @@ class badzero(int): def __cmp__(self, other): raise RuntimeError + __hash__ = None # Invalid cmp makes this unhashable self.assertRaises(RuntimeError, range, a, a + 1, badzero(1)) # Reject floats when it would require PyLongs to represent. # (smaller floats still accepted, but deprecated) - self.assertRaises(TypeError, range, 1e100, 1e101, 1e101) + with check_warnings() as w: + warnings.simplefilter("always") + self.assertRaises(TypeError, range, 1e100, 1e101, 1e101) + with check_warnings() as w: + warnings.simplefilter("always") + self.assertEqual(range(1.0), [0]) self.assertRaises(TypeError, range, 0, "spam") self.assertRaises(TypeError, range, 0, 42, "spam") @@ -1447,6 +1087,55 @@ self.assertRaises(OverflowError, range, -sys.maxint, sys.maxint) self.assertRaises(OverflowError, range, 0, 2*sys.maxint) + bignum = 2*sys.maxint + smallnum = 42 + # Old-style user-defined class with __int__ method + class I0: + def __init__(self, n): + self.n = int(n) + def __int__(self): + return self.n + self.assertEqual(range(I0(bignum), I0(bignum + 1)), [bignum]) + self.assertEqual(range(I0(smallnum), I0(smallnum + 1)), [smallnum]) + + # New-style user-defined class with __int__ method + class I1(object): + def __init__(self, n): + self.n = int(n) + def __int__(self): + return self.n + self.assertEqual(range(I1(bignum), I1(bignum + 1)), [bignum]) + self.assertEqual(range(I1(smallnum), I1(smallnum + 1)), [smallnum]) + + # New-style user-defined class with failing __int__ method + class IX(object): + def __int__(self): + raise RuntimeError + self.assertRaises(RuntimeError, range, IX()) + + # New-style user-defined class with invalid __int__ method + class IN(object): + def __int__(self): + return "not a number" + self.assertRaises(TypeError, range, IN()) + + # Exercise various combinations of bad arguments, to check + # refcounting logic + with check_warnings(): + self.assertRaises(TypeError, range, 1e100) + + self.assertRaises(TypeError, range, 0, 1e100) + self.assertRaises(TypeError, range, 1e100, 0) + self.assertRaises(TypeError, range, 1e100, 1e100) + + self.assertRaises(TypeError, range, 0, 0, 1e100) + self.assertRaises(TypeError, range, 0, 1e100, 1) + self.assertRaises(TypeError, range, 0, 1e100, 1e100) + self.assertRaises(TypeError, range, 1e100, 0, 1) + self.assertRaises(TypeError, range, 1e100, 0, 1e100) + self.assertRaises(TypeError, range, 1e100, 1e100, 1) + self.assertRaises(TypeError, range, 1e100, 1e100, 1e100) + def test_input_and_raw_input(self): self.write_testfile() fp = open(TESTFN, 'r') @@ -1554,6 +1243,7 @@ def test_round(self): self.assertEqual(round(0.0), 0.0) + self.assertEqual(type(round(0.0)), float) # Will be int in 3.0. self.assertEqual(round(1.0), 1.0) self.assertEqual(round(10.0), 10.0) self.assertEqual(round(1000000000.0), 1000000000.0) @@ -1582,32 +1272,63 @@ self.assertEqual(round(-999999999.9), -1000000000.0) self.assertEqual(round(-8.0, -1), -10.0) + self.assertEqual(type(round(-8.0, -1)), float) + + self.assertEqual(type(round(-8.0, 0)), float) + self.assertEqual(type(round(-8.0, 1)), float) + + # Check half rounding behaviour. + self.assertEqual(round(5.5), 6) + self.assertEqual(round(6.5), 7) + self.assertEqual(round(-5.5), -6) + self.assertEqual(round(-6.5), -7) + + # Check behavior on ints + self.assertEqual(round(0), 0) + self.assertEqual(round(8), 8) + self.assertEqual(round(-8), -8) + self.assertEqual(type(round(0)), float) # Will be int in 3.0. + self.assertEqual(type(round(-8, -1)), float) + self.assertEqual(type(round(-8, 0)), float) + self.assertEqual(type(round(-8, 1)), float) # test new kwargs self.assertEqual(round(number=-8.0, ndigits=-1), -10.0) self.assertRaises(TypeError, round) + # test generic rounding delegation for reals + class TestRound(object): + def __float__(self): + return 23.0 + + class TestNoRound(object): + pass + + self.assertEqual(round(TestRound()), 23) + + self.assertRaises(TypeError, round, 1, 2, 3) + self.assertRaises(TypeError, round, TestNoRound()) + + t = TestNoRound() + t.__float__ = lambda *args: args + self.assertRaises(TypeError, round, t) + self.assertRaises(TypeError, round, t, 0) + + def test_round_large(self): + # Issue #1869: integral floats should remain unchanged + self.assertEqual(round(5e15-1), 5e15-1) + self.assertEqual(round(5e15), 5e15) + self.assertEqual(round(5e15+1), 5e15+1) + self.assertEqual(round(5e15+2), 5e15+2) + self.assertEqual(round(5e15+3), 5e15+3) + def test_setattr(self): setattr(sys, 'spam', 1) self.assertEqual(sys.spam, 1) self.assertRaises(TypeError, setattr, sys, 1, 'spam') self.assertRaises(TypeError, setattr) - def test_str(self): - self.assertEqual(str(''), '') - self.assertEqual(str(0), '0') - self.assertEqual(str(0L), '0') - self.assertEqual(str(()), '()') - self.assertEqual(str([]), '[]') - self.assertEqual(str({}), '{}') - a = [] - a.append(a) - self.assertEqual(str(a), '[[...]]') - a = {} - a[0] = a - self.assertEqual(str(a), '{0: {...}}') - def test_sum(self): self.assertEqual(sum([]), 0) self.assertEqual(sum(range(2,8)), 27) @@ -1629,16 +1350,6 @@ raise ValueError self.assertRaises(ValueError, sum, BadSeq()) - def test_tuple(self): - self.assertEqual(tuple(()), ()) - t0_3 = (0, 1, 2, 3) - t0_3_bis = tuple(t0_3) - self.assert_(t0_3 is t0_3_bis) - self.assertEqual(tuple([]), ()) - self.assertEqual(tuple([0, 1, 2, 3]), (0, 1, 2, 3)) - self.assertEqual(tuple(''), ()) - self.assertEqual(tuple('spam'), ('s', 'p', 'a', 'm')) - def test_type(self): self.assertEqual(type(''), type('123')) self.assertNotEqual(type(''), type(())) @@ -1654,6 +1365,7 @@ ) self.assertRaises(ValueError, unichr, sys.maxunicode+1) self.assertRaises(TypeError, unichr) + self.assertRaises((OverflowError, ValueError), unichr, 2**32) # We don't want self in vars(), so these are static methods @@ -1720,6 +1432,115 @@ return i self.assertRaises(ValueError, zip, BadSeq(), BadSeq()) + def test_format(self): + # Test the basic machinery of the format() builtin. Don't test + # the specifics of the various formatters + self.assertEqual(format(3, ''), '3') + + # Returns some classes to use for various tests. There's + # an old-style version, and a new-style version + def classes_new(): + class A(object): + def __init__(self, x): + self.x = x + def __format__(self, format_spec): + return str(self.x) + format_spec + class DerivedFromA(A): + pass + + class Simple(object): pass + class DerivedFromSimple(Simple): + def __init__(self, x): + self.x = x + def __format__(self, format_spec): + return str(self.x) + format_spec + class DerivedFromSimple2(DerivedFromSimple): pass + return A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2 + + # In 3.0, classes_classic has the same meaning as classes_new + def classes_classic(): + class A: + def __init__(self, x): + self.x = x + def __format__(self, format_spec): + return str(self.x) + format_spec + class DerivedFromA(A): + pass + + class Simple: pass + class DerivedFromSimple(Simple): + def __init__(self, x): + self.x = x + def __format__(self, format_spec): + return str(self.x) + format_spec + class DerivedFromSimple2(DerivedFromSimple): pass + return A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2 + + def class_test(A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2): + self.assertEqual(format(A(3), 'spec'), '3spec') + self.assertEqual(format(DerivedFromA(4), 'spec'), '4spec') + self.assertEqual(format(DerivedFromSimple(5), 'abc'), '5abc') + self.assertEqual(format(DerivedFromSimple2(10), 'abcdef'), + '10abcdef') + + class_test(*classes_new()) + class_test(*classes_classic()) + + def empty_format_spec(value): + # test that: + # format(x, '') == str(x) + # format(x) == str(x) + self.assertEqual(format(value, ""), str(value)) + self.assertEqual(format(value), str(value)) + + # for builtin types, format(x, "") == str(x) + empty_format_spec(17**13) + empty_format_spec(1.0) + empty_format_spec(3.1415e104) + empty_format_spec(-3.1415e104) + empty_format_spec(3.1415e-104) + empty_format_spec(-3.1415e-104) + empty_format_spec(object) + empty_format_spec(None) + + # TypeError because self.__format__ returns the wrong type + class BadFormatResult: + def __format__(self, format_spec): + return 1.0 + self.assertRaises(TypeError, format, BadFormatResult(), "") + + # TypeError because format_spec is not unicode or str + self.assertRaises(TypeError, format, object(), 4) + self.assertRaises(TypeError, format, object(), object()) + + # tests for object.__format__ really belong elsewhere, but + # there's no good place to put them + x = object().__format__('') + self.assert_(x.startswith(' http://hg.python.org/jython/rev/589e2aa21b0d changeset: 6275:589e2aa21b0d user: Philip Jenvey date: Mon Nov 07 11:28:48 2011 -0800 summary: improve range's handling of faux-numeric types files: src/org/python/core/__builtin__.java | 39 +++++++++++---- 1 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/org/python/core/__builtin__.java b/src/org/python/core/__builtin__.java --- a/src/org/python/core/__builtin__.java +++ b/src/org/python/core/__builtin__.java @@ -904,18 +904,9 @@ * Handle range() when PyLong arguments (that OverFlow ints) are given. */ private static PyObject handleRangeLongs(PyObject ilow, PyObject ihigh, PyObject istep) { - if (!(ilow instanceof PyInteger) && !(ilow instanceof PyLong)) { - throw Py.TypeError(String.format("range() integer start argument expected, got %s.", - ilow.getType().fastGetName())); - } - if (!(ihigh instanceof PyInteger) && !(ihigh instanceof PyLong)) { - throw Py.TypeError(String.format("range() integer end argument expected, got %s.", - ihigh.getType().fastGetName())); - } - if (!(istep instanceof PyInteger) && !(istep instanceof PyLong)) { - throw Py.TypeError(String.format("range() integer step argument expected, got %s.", - istep.getType().fastGetName())); - } + ilow = getRangeLongArgument(ilow, "start"); + ihigh = getRangeLongArgument(ihigh, "end"); + istep = getRangeLongArgument(istep, "step"); int n; int cmpResult = istep._cmp(Py.Zero); @@ -964,6 +955,30 @@ } } + /** + * Helper function for handleRangeLongs. If arg is int or long object, returns it. If + * arg is float, raises type error. As a last resort, creates a new int by calling arg + * type's __int__ method if it is defined. + */ + private static PyObject getRangeLongArgument(PyObject arg, String name) { + if (arg instanceof PyInteger || arg instanceof PyLong) { + return arg; + } + + // isNumberType is roughly arg.__findattr__("__int__") != null. Equiv. to + // CPython's type.nb_init != null + if (arg instanceof PyFloat || !arg.isNumberType()) { + throw Py.TypeError(String.format("range() integer %s argument expected, got %s.", + name, arg.getType().fastGetName())); + } + + PyObject intObj = arg.__int__(); + if (intObj instanceof PyInteger || intObj instanceof PyLong) { + return intObj; + } + throw Py.TypeError("__int__ should return int object"); + } + private static PyString readline(PyObject file) { if (file instanceof PyFile) { return ((PyFile) file).readline(); -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed Nov 9 01:38:09 2011 From: jython-checkins at python.org (alan.kennedy) Date: Wed, 09 Nov 2011 01:38:09 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf8?q?=3A_merge_w/2=2E5=3A_Adding_Google_ipaddr=2Epy=2C_from_revision_?= =?utf8?q?234=2E?= Message-ID: http://hg.python.org/jython/rev/2f59a06f3e0f changeset: 6277:2f59a06f3e0f parent: 6275:589e2aa21b0d parent: 6276:49fb61a07b8d user: Alan Kennedy date: Wed Nov 09 00:37:26 2011 +0000 summary: merge w/2.5: Adding Google ipaddr.py, from revision 234. files: Lib/_google_ipaddr_r234.py | 1907 ++++++++++++++++++++++++ 1 files changed, 1907 insertions(+), 0 deletions(-) diff --git a/Lib/_google_ipaddr_r234.py b/Lib/_google_ipaddr_r234.py new file mode 100644 --- /dev/null +++ b/Lib/_google_ipaddr_r234.py @@ -0,0 +1,1907 @@ +#!/usr/bin/python +# +# Copyright 2007 Google Inc. +# Licensed to PSF under a Contributor Agreement. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""A fast, lightweight IPv4/IPv6 manipulation library in Python. + +This library is used to create/poke/manipulate IPv4 and IPv6 addresses +and networks. + +""" + +__version__ = 'trunk' + +import struct + +IPV4LENGTH = 32 +IPV6LENGTH = 128 + + +class AddressValueError(ValueError): + """A Value Error related to the address.""" + + +class NetmaskValueError(ValueError): + """A Value Error related to the netmask.""" + + +def IPAddress(address, version=None): + """Take an IP string/int and return an object of the correct type. + + Args: + address: A string or integer, the IP address. Either IPv4 or + IPv6 addresses may be supplied; integers less than 2**32 will + be considered to be IPv4 by default. + version: An Integer, 4 or 6. If set, don't try to automatically + determine what the IP address type is. important for things + like IPAddress(1), which could be IPv4, '0.0.0.1', or IPv6, + '::1'. + + Returns: + An IPv4Address or IPv6Address object. + + Raises: + ValueError: if the string passed isn't either a v4 or a v6 + address. + + """ + if version: + if version == 4: + return IPv4Address(address) + elif version == 6: + return IPv6Address(address) + + try: + return IPv4Address(address) + except (AddressValueError, NetmaskValueError): + pass + + try: + return IPv6Address(address) + except (AddressValueError, NetmaskValueError): + pass + + raise ValueError('%r does not appear to be an IPv4 or IPv6 address' % + address) + + +def IPNetwork(address, version=None, strict=False): + """Take an IP string/int and return an object of the correct type. + + Args: + address: A string or integer, the IP address. Either IPv4 or + IPv6 addresses may be supplied; integers less than 2**32 will + be considered to be IPv4 by default. + version: An Integer, if set, don't try to automatically + determine what the IP address type is. important for things + like IPNetwork(1), which could be IPv4, '0.0.0.1/32', or IPv6, + '::1/128'. + + Returns: + An IPv4Network or IPv6Network object. + + Raises: + ValueError: if the string passed isn't either a v4 or a v6 + address. Or if a strict network was requested and a strict + network wasn't given. + + """ + if version: + if version == 4: + return IPv4Network(address, strict) + elif version == 6: + return IPv6Network(address, strict) + + try: + return IPv4Network(address, strict) + except (AddressValueError, NetmaskValueError): + pass + + try: + return IPv6Network(address, strict) + except (AddressValueError, NetmaskValueError): + pass + + raise ValueError('%r does not appear to be an IPv4 or IPv6 network' % + address) + + +def v4_int_to_packed(address): + """The binary representation of this address. + + Args: + address: An integer representation of an IPv4 IP address. + + Returns: + The binary representation of this address. + + Raises: + ValueError: If the integer is too large to be an IPv4 IP + address. + """ + if address > _BaseV4._ALL_ONES: + raise ValueError('Address too large for IPv4') + return struct.pack('!I', address) + + +def v6_int_to_packed(address): + """The binary representation of this address. + + Args: + address: An integer representation of an IPv4 IP address. + + Returns: + The binary representation of this address. + """ + return struct.pack('!QQ', address >> 64, address & (2**64 - 1)) + + +def _find_address_range(addresses): + """Find a sequence of addresses. + + Args: + addresses: a list of IPv4 or IPv6 addresses. + + Returns: + A tuple containing the first and last IP addresses in the sequence. + + """ + first = last = addresses[0] + for ip in addresses[1:]: + if ip._ip == last._ip + 1: + last = ip + else: + break + return (first, last) + +def _get_prefix_length(number1, number2, bits): + """Get the number of leading bits that are same for two numbers. + + Args: + number1: an integer. + number2: another integer. + bits: the maximum number of bits to compare. + + Returns: + The number of leading bits that are the same for two numbers. + + """ + for i in range(bits): + if number1 >> i == number2 >> i: + return bits - i + return 0 + +def _count_righthand_zero_bits(number, bits): + """Count the number of zero bits on the right hand side. + + Args: + number: an integer. + bits: maximum number of bits to count. + + Returns: + The number of zero bits on the right hand side of the number. + + """ + if number == 0: + return bits + for i in range(bits): + if (number >> i) % 2: + return i + +def summarize_address_range(first, last): + """Summarize a network range given the first and last IP addresses. + + Example: + >>> summarize_address_range(IPv4Address('1.1.1.0'), + IPv4Address('1.1.1.130')) + [IPv4Network('1.1.1.0/25'), IPv4Network('1.1.1.128/31'), + IPv4Network('1.1.1.130/32')] + + Args: + first: the first IPv4Address or IPv6Address in the range. + last: the last IPv4Address or IPv6Address in the range. + + Returns: + The address range collapsed to a list of IPv4Network's or + IPv6Network's. + + Raise: + TypeError: + If the first and last objects are not IP addresses. + If the first and last objects are not the same version. + ValueError: + If the last object is not greater than the first. + If the version is not 4 or 6. + + """ + if not (isinstance(first, _BaseIP) and isinstance(last, _BaseIP)): + raise TypeError('first and last must be IP addresses, not networks') + if first.version != last.version: + raise TypeError("%s and %s are not of the same version" % ( + str(first), str(last))) + if first > last: + raise ValueError('last IP address must be greater than first') + + networks = [] + + if first.version == 4: + ip = IPv4Network + elif first.version == 6: + ip = IPv6Network + else: + raise ValueError('unknown IP version') + + ip_bits = first._max_prefixlen + first_int = first._ip + last_int = last._ip + while first_int <= last_int: + nbits = _count_righthand_zero_bits(first_int, ip_bits) + current = None + while nbits >= 0: + addend = 2**nbits - 1 + current = first_int + addend + nbits -= 1 + if current <= last_int: + break + prefix = _get_prefix_length(first_int, current, ip_bits) + net = ip('%s/%d' % (str(first), prefix)) + networks.append(net) + if current == ip._ALL_ONES: + break + first_int = current + 1 + first = IPAddress(first_int, version=first._version) + return networks + +def _collapse_address_list_recursive(addresses): + """Loops through the addresses, collapsing concurrent netblocks. + + Example: + + ip1 = IPv4Network('1.1.0.0/24') + ip2 = IPv4Network('1.1.1.0/24') + ip3 = IPv4Network('1.1.2.0/24') + ip4 = IPv4Network('1.1.3.0/24') + ip5 = IPv4Network('1.1.4.0/24') + ip6 = IPv4Network('1.1.0.1/22') + + _collapse_address_list_recursive([ip1, ip2, ip3, ip4, ip5, ip6]) -> + [IPv4Network('1.1.0.0/22'), IPv4Network('1.1.4.0/24')] + + This shouldn't be called directly; it is called via + collapse_address_list([]). + + Args: + addresses: A list of IPv4Network's or IPv6Network's + + Returns: + A list of IPv4Network's or IPv6Network's depending on what we were + passed. + + """ + ret_array = [] + optimized = False + + for cur_addr in addresses: + if not ret_array: + ret_array.append(cur_addr) + continue + if cur_addr in ret_array[-1]: + optimized = True + elif cur_addr == ret_array[-1].supernet().subnet()[1]: + ret_array.append(ret_array.pop().supernet()) + optimized = True + else: + ret_array.append(cur_addr) + + if optimized: + return _collapse_address_list_recursive(ret_array) + + return ret_array + + +def collapse_address_list(addresses): + """Collapse a list of IP objects. + + Example: + collapse_address_list([IPv4('1.1.0.0/24'), IPv4('1.1.1.0/24')]) -> + [IPv4('1.1.0.0/23')] + + Args: + addresses: A list of IPv4Network or IPv6Network objects. + + Returns: + A list of IPv4Network or IPv6Network objects depending on what we + were passed. + + Raises: + TypeError: If passed a list of mixed version objects. + + """ + i = 0 + addrs = [] + ips = [] + nets = [] + + # split IP addresses and networks + for ip in addresses: + if isinstance(ip, _BaseIP): + if ips and ips[-1]._version != ip._version: + raise TypeError("%s and %s are not of the same version" % ( + str(ip), str(ips[-1]))) + ips.append(ip) + elif ip._prefixlen == ip._max_prefixlen: + if ips and ips[-1]._version != ip._version: + raise TypeError("%s and %s are not of the same version" % ( + str(ip), str(ips[-1]))) + ips.append(ip.ip) + else: + if nets and nets[-1]._version != ip._version: + raise TypeError("%s and %s are not of the same version" % ( + str(ip), str(ips[-1]))) + nets.append(ip) + + # sort and dedup + ips = sorted(set(ips)) + nets = sorted(set(nets)) + + while i < len(ips): + (first, last) = _find_address_range(ips[i:]) + i = ips.index(last) + 1 + addrs.extend(summarize_address_range(first, last)) + + return _collapse_address_list_recursive(sorted( + addrs + nets, key=_BaseNet._get_networks_key)) + +# backwards compatibility +CollapseAddrList = collapse_address_list + +# Test whether this Python implementation supports byte objects that +# are not identical to str ones. +# We need to exclude platforms where bytes == str so that we can +# distinguish between packed representations and strings, for example +# b'12::' (the IPv4 address 49.50.58.58) and '12::' (an IPv6 address). +try: + _compat_has_real_bytes = bytes is not str +except NameError: # other._ip + return False + + # Shorthand for Integer addition and subtraction. This is not + # meant to ever support addition/subtraction of addresses. + def __add__(self, other): + if not isinstance(other, int): + return NotImplemented + return IPAddress(int(self) + other, version=self._version) + + def __sub__(self, other): + if not isinstance(other, int): + return NotImplemented + return IPAddress(int(self) - other, version=self._version) + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, str(self)) + + def __str__(self): + return '%s' % self._string_from_ip_int(self._ip) + + def __hash__(self): + return hash(hex(long(self._ip))) + + def _get_address_key(self): + return (self._version, self) + + @property + def version(self): + raise NotImplementedError('BaseIP has no version') + + +class _BaseNet(_IPAddrBase): + + """A generic IP object. + + This IP class contains the version independent methods which are + used by networks. + + """ + + def __init__(self, address): + self._cache = {} + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, str(self)) + + def iterhosts(self): + """Generate Iterator over usable hosts in a network. + + This is like __iter__ except it doesn't return the network + or broadcast addresses. + + """ + cur = int(self.network) + 1 + bcast = int(self.broadcast) - 1 + while cur <= bcast: + cur += 1 + yield IPAddress(cur - 1, version=self._version) + + def __iter__(self): + cur = int(self.network) + bcast = int(self.broadcast) + while cur <= bcast: + cur += 1 + yield IPAddress(cur - 1, version=self._version) + + def __getitem__(self, n): + network = int(self.network) + broadcast = int(self.broadcast) + if n >= 0: + if network + n > broadcast: + raise IndexError + return IPAddress(network + n, version=self._version) + else: + n += 1 + if broadcast + n < network: + raise IndexError + return IPAddress(broadcast + n, version=self._version) + + def __lt__(self, other): + if self._version != other._version: + raise TypeError('%s and %s are not of the same version' % ( + str(self), str(other))) + if not isinstance(other, _BaseNet): + raise TypeError('%s and %s are not of the same type' % ( + str(self), str(other))) + if self.network != other.network: + return self.network < other.network + if self.netmask != other.netmask: + return self.netmask < other.netmask + return False + + def __gt__(self, other): + if self._version != other._version: + raise TypeError('%s and %s are not of the same version' % ( + str(self), str(other))) + if not isinstance(other, _BaseNet): + raise TypeError('%s and %s are not of the same type' % ( + str(self), str(other))) + if self.network != other.network: + return self.network > other.network + if self.netmask != other.netmask: + return self.netmask > other.netmask + return False + + def __le__(self, other): + gt = self.__gt__(other) + if gt is NotImplemented: + return NotImplemented + return not gt + + def __ge__(self, other): + lt = self.__lt__(other) + if lt is NotImplemented: + return NotImplemented + return not lt + + def __eq__(self, other): + try: + return (self._version == other._version + and self.network == other.network + and int(self.netmask) == int(other.netmask)) + except AttributeError: + if isinstance(other, _BaseIP): + return (self._version == other._version + and self._ip == other._ip) + + def __ne__(self, other): + eq = self.__eq__(other) + if eq is NotImplemented: + return NotImplemented + return not eq + + def __str__(self): + return '%s/%s' % (str(self.ip), + str(self._prefixlen)) + + def __hash__(self): + return hash(int(self.network) ^ int(self.netmask)) + + def __contains__(self, other): + # always false if one is v4 and the other is v6. + if self._version != other._version: + return False + # dealing with another network. + if isinstance(other, _BaseNet): + return (self.network <= other.network and + self.broadcast >= other.broadcast) + # dealing with another address + else: + return (int(self.network) <= int(other._ip) <= + int(self.broadcast)) + + def overlaps(self, other): + """Tell if self is partly contained in other.""" + return self.network in other or self.broadcast in other or ( + other.network in self or other.broadcast in self) + + @property + def network(self): + x = self._cache.get('network') + if x is None: + x = IPAddress(self._ip & int(self.netmask), version=self._version) + self._cache['network'] = x + return x + + @property + def broadcast(self): + x = self._cache.get('broadcast') + if x is None: + x = IPAddress(self._ip | int(self.hostmask), version=self._version) + self._cache['broadcast'] = x + return x + + @property + def hostmask(self): + x = self._cache.get('hostmask') + if x is None: + x = IPAddress(int(self.netmask) ^ self._ALL_ONES, + version=self._version) + self._cache['hostmask'] = x + return x + + @property + def with_prefixlen(self): + return '%s/%d' % (str(self.ip), self._prefixlen) + + @property + def with_netmask(self): + return '%s/%s' % (str(self.ip), str(self.netmask)) + + @property + def with_hostmask(self): + return '%s/%s' % (str(self.ip), str(self.hostmask)) + + @property + def numhosts(self): + """Number of hosts in the current subnet.""" + return int(self.broadcast) - int(self.network) + 1 + + @property + def version(self): + raise NotImplementedError('BaseNet has no version') + + @property + def prefixlen(self): + return self._prefixlen + + def address_exclude(self, other): + """Remove an address from a larger block. + + For example: + + addr1 = IPNetwork('10.1.1.0/24') + addr2 = IPNetwork('10.1.1.0/26') + addr1.address_exclude(addr2) = + [IPNetwork('10.1.1.64/26'), IPNetwork('10.1.1.128/25')] + + or IPv6: + + addr1 = IPNetwork('::1/32') + addr2 = IPNetwork('::1/128') + addr1.address_exclude(addr2) = [IPNetwork('::0/128'), + IPNetwork('::2/127'), + IPNetwork('::4/126'), + IPNetwork('::8/125'), + ... + IPNetwork('0:0:8000::/33')] + + Args: + other: An IPvXNetwork object of the same type. + + Returns: + A sorted list of IPvXNetwork objects addresses which is self + minus other. + + Raises: + TypeError: If self and other are of difffering address + versions, or if other is not a network object. + ValueError: If other is not completely contained by self. + + """ + if not self._version == other._version: + raise TypeError("%s and %s are not of the same version" % ( + str(self), str(other))) + + if not isinstance(other, _BaseNet): + raise TypeError("%s is not a network object" % str(other)) + + if other not in self: + raise ValueError('%s not contained in %s' % (str(other), + str(self))) + if other == self: + return [] + + ret_addrs = [] + + # Make sure we're comparing the network of other. + other = IPNetwork('%s/%s' % (str(other.network), str(other.prefixlen)), + version=other._version) + + s1, s2 = self.subnet() + while s1 != other and s2 != other: + if other in s1: + ret_addrs.append(s2) + s1, s2 = s1.subnet() + elif other in s2: + ret_addrs.append(s1) + s1, s2 = s2.subnet() + else: + # If we got here, there's a bug somewhere. + assert True == False, ('Error performing exclusion: ' + 's1: %s s2: %s other: %s' % + (str(s1), str(s2), str(other))) + if s1 == other: + ret_addrs.append(s2) + elif s2 == other: + ret_addrs.append(s1) + else: + # If we got here, there's a bug somewhere. + assert True == False, ('Error performing exclusion: ' + 's1: %s s2: %s other: %s' % + (str(s1), str(s2), str(other))) + + return sorted(ret_addrs, key=_BaseNet._get_networks_key) + + def compare_networks(self, other): + """Compare two IP objects. + + This is only concerned about the comparison of the integer + representation of the network addresses. This means that the + host bits aren't considered at all in this method. If you want + to compare host bits, you can easily enough do a + 'HostA._ip < HostB._ip' + + Args: + other: An IP object. + + Returns: + If the IP versions of self and other are the same, returns: + + -1 if self < other: + eg: IPv4('1.1.1.0/24') < IPv4('1.1.2.0/24') + IPv6('1080::200C:417A') < IPv6('1080::200B:417B') + 0 if self == other + eg: IPv4('1.1.1.1/24') == IPv4('1.1.1.2/24') + IPv6('1080::200C:417A/96') == IPv6('1080::200C:417B/96') + 1 if self > other + eg: IPv4('1.1.1.0/24') > IPv4('1.1.0.0/24') + IPv6('1080::1:200C:417A/112') > + IPv6('1080::0:200C:417A/112') + + If the IP versions of self and other are different, returns: + + -1 if self._version < other._version + eg: IPv4('10.0.0.1/24') < IPv6('::1/128') + 1 if self._version > other._version + eg: IPv6('::1/128') > IPv4('255.255.255.0/24') + + """ + if self._version < other._version: + return -1 + if self._version > other._version: + return 1 + # self._version == other._version below here: + if self.network < other.network: + return -1 + if self.network > other.network: + return 1 + # self.network == other.network below here: + if self.netmask < other.netmask: + return -1 + if self.netmask > other.netmask: + return 1 + # self.network == other.network and self.netmask == other.netmask + return 0 + + def _get_networks_key(self): + """Network-only key function. + + Returns an object that identifies this address' network and + netmask. This function is a suitable "key" argument for sorted() + and list.sort(). + + """ + return (self._version, self.network, self.netmask) + + def _ip_int_from_prefix(self, prefixlen=None): + """Turn the prefix length netmask into a int for comparison. + + Args: + prefixlen: An integer, the prefix length. + + Returns: + An integer. + + """ + if not prefixlen and prefixlen != 0: + prefixlen = self._prefixlen + return self._ALL_ONES ^ (self._ALL_ONES >> prefixlen) + + def _prefix_from_ip_int(self, ip_int, mask=32): + """Return prefix length from the decimal netmask. + + Args: + ip_int: An integer, the IP address. + mask: The netmask. Defaults to 32. + + Returns: + An integer, the prefix length. + + """ + while mask: + if ip_int & 1 == 1: + break + ip_int >>= 1 + mask -= 1 + + return mask + + def _ip_string_from_prefix(self, prefixlen=None): + """Turn a prefix length into a dotted decimal string. + + Args: + prefixlen: An integer, the netmask prefix length. + + Returns: + A string, the dotted decimal netmask string. + + """ + if not prefixlen: + prefixlen = self._prefixlen + return self._string_from_ip_int(self._ip_int_from_prefix(prefixlen)) + + def iter_subnets(self, prefixlen_diff=1, new_prefix=None): + """The subnets which join to make the current subnet. + + In the case that self contains only one IP + (self._prefixlen == 32 for IPv4 or self._prefixlen == 128 + for IPv6), return a list with just ourself. + + Args: + prefixlen_diff: An integer, the amount the prefix length + should be increased by. This should not be set if + new_prefix is also set. + new_prefix: The desired new prefix length. This must be a + larger number (smaller prefix) than the existing prefix. + This should not be set if prefixlen_diff is also set. + + Returns: + An iterator of IPv(4|6) objects. + + Raises: + ValueError: The prefixlen_diff is too small or too large. + OR + prefixlen_diff and new_prefix are both set or new_prefix + is a smaller number than the current prefix (smaller + number means a larger network) + + """ + if self._prefixlen == self._max_prefixlen: + yield self + return + + if new_prefix is not None: + if new_prefix < self._prefixlen: + raise ValueError('new prefix must be longer') + if prefixlen_diff != 1: + raise ValueError('cannot set prefixlen_diff and new_prefix') + prefixlen_diff = new_prefix - self._prefixlen + + if prefixlen_diff < 0: + raise ValueError('prefix length diff must be > 0') + new_prefixlen = self._prefixlen + prefixlen_diff + + if not self._is_valid_netmask(str(new_prefixlen)): + raise ValueError( + 'prefix length diff %d is invalid for netblock %s' % ( + new_prefixlen, str(self))) + + first = IPNetwork('%s/%s' % (str(self.network), + str(self._prefixlen + prefixlen_diff)), + version=self._version) + + yield first + current = first + while True: + broadcast = current.broadcast + if broadcast == self.broadcast: + return + new_addr = IPAddress(int(broadcast) + 1, version=self._version) + current = IPNetwork('%s/%s' % (str(new_addr), str(new_prefixlen)), + version=self._version) + + yield current + + def masked(self): + """Return the network object with the host bits masked out.""" + return IPNetwork('%s/%d' % (self.network, self._prefixlen), + version=self._version) + + def subnet(self, prefixlen_diff=1, new_prefix=None): + """Return a list of subnets, rather than an iterator.""" + return list(self.iter_subnets(prefixlen_diff, new_prefix)) + + def supernet(self, prefixlen_diff=1, new_prefix=None): + """The supernet containing the current network. + + Args: + prefixlen_diff: An integer, the amount the prefix length of + the network should be decreased by. For example, given a + /24 network and a prefixlen_diff of 3, a supernet with a + /21 netmask is returned. + + Returns: + An IPv4 network object. + + Raises: + ValueError: If self.prefixlen - prefixlen_diff < 0. I.e., you have a + negative prefix length. + OR + If prefixlen_diff and new_prefix are both set or new_prefix is a + larger number than the current prefix (larger number means a + smaller network) + + """ + if self._prefixlen == 0: + return self + + if new_prefix is not None: + if new_prefix > self._prefixlen: + raise ValueError('new prefix must be shorter') + if prefixlen_diff != 1: + raise ValueError('cannot set prefixlen_diff and new_prefix') + prefixlen_diff = self._prefixlen - new_prefix + + + if self.prefixlen - prefixlen_diff < 0: + raise ValueError( + 'current prefixlen is %d, cannot have a prefixlen_diff of %d' % + (self.prefixlen, prefixlen_diff)) + return IPNetwork('%s/%s' % (str(self.network), + str(self.prefixlen - prefixlen_diff)), + version=self._version) + + # backwards compatibility + Subnet = subnet + Supernet = supernet + AddressExclude = address_exclude + CompareNetworks = compare_networks + Contains = __contains__ + + +class _BaseV4(object): + + """Base IPv4 object. + + The following methods are used by IPv4 objects in both single IP + addresses and networks. + + """ + + # Equivalent to 255.255.255.255 or 32 bits of 1's. + _ALL_ONES = (2**IPV4LENGTH) - 1 + _DECIMAL_DIGITS = frozenset('0123456789') + + def __init__(self, address): + self._version = 4 + self._max_prefixlen = IPV4LENGTH + + def _explode_shorthand_ip_string(self, ip_str=None): + if not ip_str: + ip_str = str(self) + return ip_str + + def _ip_int_from_string(self, ip_str): + """Turn the given IP string into an integer for comparison. + + Args: + ip_str: A string, the IP ip_str. + + Returns: + The IP ip_str as an integer. + + Raises: + AddressValueError: if ip_str isn't a valid IPv4 Address. + + """ + octets = ip_str.split('.') + if len(octets) != 4: + raise AddressValueError(ip_str) + + packed_ip = 0 + for oc in octets: + try: + packed_ip = (packed_ip << 8) | self._parse_octet(oc) + except ValueError: + raise AddressValueError(ip_str) + return packed_ip + + def _parse_octet(self, octet_str): + """Convert a decimal octet into an integer. + + Args: + octet_str: A string, the number to parse. + + Returns: + The octet as an integer. + + Raises: + ValueError: if the octet isn't strictly a decimal from [0..255]. + + """ + # Whitelist the characters, since int() allows a lot of bizarre stuff. + if not self._DECIMAL_DIGITS.issuperset(octet_str): + raise ValueError + octet_int = int(octet_str, 10) + # Disallow leading zeroes, because no clear standard exists on + # whether these should be interpreted as decimal or octal. + if octet_int > 255 or (octet_str[0] == '0' and len(octet_str) > 1): + raise ValueError + return octet_int + + def _string_from_ip_int(self, ip_int): + """Turns a 32-bit integer into dotted decimal notation. + + Args: + ip_int: An integer, the IP address. + + Returns: + The IP address as a string in dotted decimal notation. + + """ + octets = [] + for _ in xrange(4): + octets.insert(0, str(ip_int & 0xFF)) + ip_int >>= 8 + return '.'.join(octets) + + @property + def max_prefixlen(self): + return self._max_prefixlen + + @property + def packed(self): + """The binary representation of this address.""" + return v4_int_to_packed(self._ip) + + @property + def version(self): + return self._version + + @property + def is_reserved(self): + """Test if the address is otherwise IETF reserved. + + Returns: + A boolean, True if the address is within the + reserved IPv4 Network range. + + """ + return self in IPv4Network('240.0.0.0/4') + + @property + def is_private(self): + """Test if this address is allocated for private networks. + + Returns: + A boolean, True if the address is reserved per RFC 1918. + + """ + return (self in IPv4Network('10.0.0.0/8') or + self in IPv4Network('172.16.0.0/12') or + self in IPv4Network('192.168.0.0/16')) + + @property + def is_multicast(self): + """Test if the address is reserved for multicast use. + + Returns: + A boolean, True if the address is multicast. + See RFC 3171 for details. + + """ + return self in IPv4Network('224.0.0.0/4') + + @property + def is_unspecified(self): + """Test if the address is unspecified. + + Returns: + A boolean, True if this is the unspecified address as defined in + RFC 5735 3. + + """ + return self in IPv4Network('0.0.0.0') + + @property + def is_loopback(self): + """Test if the address is a loopback address. + + Returns: + A boolean, True if the address is a loopback per RFC 3330. + + """ + return self in IPv4Network('127.0.0.0/8') + + @property + def is_link_local(self): + """Test if the address is reserved for link-local. + + Returns: + A boolean, True if the address is link-local per RFC 3927. + + """ + return self in IPv4Network('169.254.0.0/16') + + +class IPv4Address(_BaseV4, _BaseIP): + + """Represent and manipulate single IPv4 Addresses.""" + + def __init__(self, address): + + """ + Args: + address: A string or integer representing the IP + '192.168.1.1' + + Additionally, an integer can be passed, so + IPv4Address('192.168.1.1') == IPv4Address(3232235777). + or, more generally + IPv4Address(int(IPv4Address('192.168.1.1'))) == + IPv4Address('192.168.1.1') + + Raises: + AddressValueError: If ipaddr isn't a valid IPv4 address. + + """ + _BaseIP.__init__(self, address) + _BaseV4.__init__(self, address) + + # Efficient constructor from integer. + if isinstance(address, (int, long)): + self._ip = address + if address < 0 or address > self._ALL_ONES: + raise AddressValueError(address) + return + + # Constructing from a packed address + if _compat_has_real_bytes: + if isinstance(address, bytes) and len(address) == 4: + self._ip = struct.unpack('!I', address)[0] + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP string. + addr_str = str(address) + self._ip = self._ip_int_from_string(addr_str) + + +class IPv4Network(_BaseV4, _BaseNet): + + """This class represents and manipulates 32-bit IPv4 networks. + + Attributes: [examples for IPv4Network('1.2.3.4/27')] + ._ip: 16909060 + .ip: IPv4Address('1.2.3.4') + .network: IPv4Address('1.2.3.0') + .hostmask: IPv4Address('0.0.0.31') + .broadcast: IPv4Address('1.2.3.31') + .netmask: IPv4Address('255.255.255.224') + .prefixlen: 27 + + """ + + # the valid octets for host and netmasks. only useful for IPv4. + _valid_mask_octets = set((255, 254, 252, 248, 240, 224, 192, 128, 0)) + + def __init__(self, address, strict=False): + """Instantiate a new IPv4 network object. + + Args: + address: A string or integer representing the IP [& network]. + '192.168.1.1/24' + '192.168.1.1/255.255.255.0' + '192.168.1.1/0.0.0.255' + are all functionally the same in IPv4. Similarly, + '192.168.1.1' + '192.168.1.1/255.255.255.255' + '192.168.1.1/32' + are also functionaly equivalent. That is to say, failing to + provide a subnetmask will create an object with a mask of /32. + + If the mask (portion after the / in the argument) is given in + dotted quad form, it is treated as a netmask if it starts with a + non-zero field (e.g. /255.0.0.0 == /8) and as a hostmask if it + starts with a zero field (e.g. 0.255.255.255 == /8), with the + single exception of an all-zero mask which is treated as a + netmask == /0. If no mask is given, a default of /32 is used. + + Additionally, an integer can be passed, so + IPv4Network('192.168.1.1') == IPv4Network(3232235777). + or, more generally + IPv4Network(int(IPv4Network('192.168.1.1'))) == + IPv4Network('192.168.1.1') + + strict: A boolean. If true, ensure that we have been passed + A true network address, eg, 192.168.1.0/24 and not an + IP address on a network, eg, 192.168.1.1/24. + + Raises: + AddressValueError: If ipaddr isn't a valid IPv4 address. + NetmaskValueError: If the netmask isn't valid for + an IPv4 address. + ValueError: If strict was True and a network address was not + supplied. + + """ + _BaseNet.__init__(self, address) + _BaseV4.__init__(self, address) + + # Efficient constructor from integer. + if isinstance(address, (int, long)): + self._ip = address + self.ip = IPv4Address(self._ip) + self._prefixlen = self._max_prefixlen + self.netmask = IPv4Address(self._ALL_ONES) + if address < 0 or address > self._ALL_ONES: + raise AddressValueError(address) + return + + # Constructing from a packed address + if _compat_has_real_bytes: + if isinstance(address, bytes) and len(address) == 4: + self._ip = struct.unpack('!I', address)[0] + self.ip = IPv4Address(self._ip) + self._prefixlen = self._max_prefixlen + self.netmask = IPv4Address(self._ALL_ONES) + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP prefix string. + addr = str(address).split('/') + + if len(addr) > 2: + raise AddressValueError(address) + + self._ip = self._ip_int_from_string(addr[0]) + self.ip = IPv4Address(self._ip) + + if len(addr) == 2: + mask = addr[1].split('.') + if len(mask) == 4: + # We have dotted decimal netmask. + if self._is_valid_netmask(addr[1]): + self.netmask = IPv4Address(self._ip_int_from_string( + addr[1])) + elif self._is_hostmask(addr[1]): + self.netmask = IPv4Address( + self._ip_int_from_string(addr[1]) ^ self._ALL_ONES) + else: + raise NetmaskValueError('%s is not a valid netmask' + % addr[1]) + + self._prefixlen = self._prefix_from_ip_int(int(self.netmask)) + else: + # We have a netmask in prefix length form. + if not self._is_valid_netmask(addr[1]): + raise NetmaskValueError(addr[1]) + self._prefixlen = int(addr[1]) + self.netmask = IPv4Address(self._ip_int_from_prefix( + self._prefixlen)) + else: + self._prefixlen = self._max_prefixlen + self.netmask = IPv4Address(self._ip_int_from_prefix( + self._prefixlen)) + if strict: + if self.ip != self.network: + raise ValueError('%s has host bits set' % + self.ip) + + def _is_hostmask(self, ip_str): + """Test if the IP string is a hostmask (rather than a netmask). + + Args: + ip_str: A string, the potential hostmask. + + Returns: + A boolean, True if the IP string is a hostmask. + + """ + bits = ip_str.split('.') + try: + parts = [int(x) for x in bits if int(x) in self._valid_mask_octets] + except ValueError: + return False + if len(parts) != len(bits): + return False + if parts[0] < parts[-1]: + return True + return False + + def _is_valid_netmask(self, netmask): + """Verify that the netmask is valid. + + Args: + netmask: A string, either a prefix or dotted decimal + netmask. + + Returns: + A boolean, True if the prefix represents a valid IPv4 + netmask. + + """ + mask = netmask.split('.') + if len(mask) == 4: + if [x for x in mask if int(x) not in self._valid_mask_octets]: + return False + if [y for idx, y in enumerate(mask) if idx > 0 and + y > mask[idx - 1]]: + return False + return True + try: + netmask = int(netmask) + except ValueError: + return False + return 0 <= netmask <= self._max_prefixlen + + # backwards compatibility + IsRFC1918 = lambda self: self.is_private + IsMulticast = lambda self: self.is_multicast + IsLoopback = lambda self: self.is_loopback + IsLinkLocal = lambda self: self.is_link_local + + +class _BaseV6(object): + + """Base IPv6 object. + + The following methods are used by IPv6 objects in both single IP + addresses and networks. + + """ + + _ALL_ONES = (2**IPV6LENGTH) - 1 + _HEXTET_COUNT = 8 + _HEX_DIGITS = frozenset('0123456789ABCDEFabcdef') + + def __init__(self, address): + self._version = 6 + self._max_prefixlen = IPV6LENGTH + + def _ip_int_from_string(self, ip_str): + """Turn an IPv6 ip_str into an integer. + + Args: + ip_str: A string, the IPv6 ip_str. + + Returns: + A long, the IPv6 ip_str. + + Raises: + AddressValueError: if ip_str isn't a valid IPv6 Address. + + """ + parts = ip_str.split(':') + + # An IPv6 address needs at least 2 colons (3 parts). + if len(parts) < 3: + raise AddressValueError(ip_str) + + # If the address has an IPv4-style suffix, convert it to hexadecimal. + if '.' in parts[-1]: + ipv4_int = IPv4Address(parts.pop())._ip + parts.append('%x' % ((ipv4_int >> 16) & 0xFFFF)) + parts.append('%x' % (ipv4_int & 0xFFFF)) + + # An IPv6 address can't have more than 8 colons (9 parts). + if len(parts) > self._HEXTET_COUNT + 1: + raise AddressValueError(ip_str) + + # Disregarding the endpoints, find '::' with nothing in between. + # This indicates that a run of zeroes has been skipped. + try: + skip_index, = ( + [i for i in xrange(1, len(parts) - 1) if not parts[i]] or + [None]) + except ValueError: + # Can't have more than one '::' + raise AddressValueError(ip_str) + + # parts_hi is the number of parts to copy from above/before the '::' + # parts_lo is the number of parts to copy from below/after the '::' + if skip_index is not None: + # If we found a '::', then check if it also covers the endpoints. + parts_hi = skip_index + parts_lo = len(parts) - skip_index - 1 + if not parts[0]: + parts_hi -= 1 + if parts_hi: + raise AddressValueError(ip_str) # ^: requires ^:: + if not parts[-1]: + parts_lo -= 1 + if parts_lo: + raise AddressValueError(ip_str) # :$ requires ::$ + parts_skipped = self._HEXTET_COUNT - (parts_hi + parts_lo) + if parts_skipped < 1: + raise AddressValueError(ip_str) + else: + # Otherwise, allocate the entire address to parts_hi. The endpoints + # could still be empty, but _parse_hextet() will check for that. + if len(parts) != self._HEXTET_COUNT: + raise AddressValueError(ip_str) + parts_hi = len(parts) + parts_lo = 0 + parts_skipped = 0 + + try: + # Now, parse the hextets into a 128-bit integer. + ip_int = 0L + for i in xrange(parts_hi): + ip_int <<= 16 + ip_int |= self._parse_hextet(parts[i]) + ip_int <<= 16 * parts_skipped + for i in xrange(-parts_lo, 0): + ip_int <<= 16 + ip_int |= self._parse_hextet(parts[i]) + return ip_int + except ValueError: + raise AddressValueError(ip_str) + + def _parse_hextet(self, hextet_str): + """Convert an IPv6 hextet string into an integer. + + Args: + hextet_str: A string, the number to parse. + + Returns: + The hextet as an integer. + + Raises: + ValueError: if the input isn't strictly a hex number from [0..FFFF]. + + """ + # Whitelist the characters, since int() allows a lot of bizarre stuff. + if not self._HEX_DIGITS.issuperset(hextet_str): + raise ValueError + hextet_int = int(hextet_str, 16) + if hextet_int > 0xFFFF: + raise ValueError + return hextet_int + + def _compress_hextets(self, hextets): + """Compresses a list of hextets. + + Compresses a list of strings, replacing the longest continuous + sequence of "0" in the list with "" and adding empty strings at + the beginning or at the end of the string such that subsequently + calling ":".join(hextets) will produce the compressed version of + the IPv6 address. + + Args: + hextets: A list of strings, the hextets to compress. + + Returns: + A list of strings. + + """ + best_doublecolon_start = -1 + best_doublecolon_len = 0 + doublecolon_start = -1 + doublecolon_len = 0 + for index in range(len(hextets)): + if hextets[index] == '0': + doublecolon_len += 1 + if doublecolon_start == -1: + # Start of a sequence of zeros. + doublecolon_start = index + if doublecolon_len > best_doublecolon_len: + # This is the longest sequence of zeros so far. + best_doublecolon_len = doublecolon_len + best_doublecolon_start = doublecolon_start + else: + doublecolon_len = 0 + doublecolon_start = -1 + + if best_doublecolon_len > 1: + best_doublecolon_end = (best_doublecolon_start + + best_doublecolon_len) + # For zeros at the end of the address. + if best_doublecolon_end == len(hextets): + hextets += [''] + hextets[best_doublecolon_start:best_doublecolon_end] = [''] + # For zeros at the beginning of the address. + if best_doublecolon_start == 0: + hextets = [''] + hextets + + return hextets + + def _string_from_ip_int(self, ip_int=None): + """Turns a 128-bit integer into hexadecimal notation. + + Args: + ip_int: An integer, the IP address. + + Returns: + A string, the hexadecimal representation of the address. + + Raises: + ValueError: The address is bigger than 128 bits of all ones. + + """ + if not ip_int and ip_int != 0: + ip_int = int(self._ip) + + if ip_int > self._ALL_ONES: + raise ValueError('IPv6 address is too large') + + hex_str = '%032x' % ip_int + hextets = [] + for x in range(0, 32, 4): + hextets.append('%x' % int(hex_str[x:x+4], 16)) + + hextets = self._compress_hextets(hextets) + return ':'.join(hextets) + + def _explode_shorthand_ip_string(self, ip_str=None): + """Expand a shortened IPv6 address. + + Args: + ip_str: A string, the IPv6 address. + + Returns: + A string, the expanded IPv6 address. + + """ + if not ip_str: + ip_str = str(self) + if isinstance(self, _BaseNet): + ip_str = str(self.ip) + + ip_int = self._ip_int_from_string(ip_str) + parts = [] + for i in xrange(self._HEXTET_COUNT): + parts.append('%04x' % (ip_int & 0xFFFF)) + ip_int >>= 16 + parts.reverse() + return ':'.join(parts) + + @property + def max_prefixlen(self): + return self._max_prefixlen + + @property + def packed(self): + """The binary representation of this address.""" + return v6_int_to_packed(self._ip) + + @property + def version(self): + return self._version + + @property + def is_multicast(self): + """Test if the address is reserved for multicast use. + + Returns: + A boolean, True if the address is a multicast address. + See RFC 2373 2.7 for details. + + """ + return self in IPv6Network('ff00::/8') + + @property + def is_reserved(self): + """Test if the address is otherwise IETF reserved. + + Returns: + A boolean, True if the address is within one of the + reserved IPv6 Network ranges. + + """ + return (self in IPv6Network('::/8') or + self in IPv6Network('100::/8') or + self in IPv6Network('200::/7') or + self in IPv6Network('400::/6') or + self in IPv6Network('800::/5') or + self in IPv6Network('1000::/4') or + self in IPv6Network('4000::/3') or + self in IPv6Network('6000::/3') or + self in IPv6Network('8000::/3') or + self in IPv6Network('A000::/3') or + self in IPv6Network('C000::/3') or + self in IPv6Network('E000::/4') or + self in IPv6Network('F000::/5') or + self in IPv6Network('F800::/6') or + self in IPv6Network('FE00::/9')) + + @property + def is_unspecified(self): + """Test if the address is unspecified. + + Returns: + A boolean, True if this is the unspecified address as defined in + RFC 2373 2.5.2. + + """ + return self._ip == 0 and getattr(self, '_prefixlen', 128) == 128 + + @property + def is_loopback(self): + """Test if the address is a loopback address. + + Returns: + A boolean, True if the address is a loopback address as defined in + RFC 2373 2.5.3. + + """ + return self._ip == 1 and getattr(self, '_prefixlen', 128) == 128 + + @property + def is_link_local(self): + """Test if the address is reserved for link-local. + + Returns: + A boolean, True if the address is reserved per RFC 4291. + + """ + return self in IPv6Network('fe80::/10') + + @property + def is_site_local(self): + """Test if the address is reserved for site-local. + + Note that the site-local address space has been deprecated by RFC 3879. + Use is_private to test if this address is in the space of unique local + addresses as defined by RFC 4193. + + Returns: + A boolean, True if the address is reserved per RFC 3513 2.5.6. + + """ + return self in IPv6Network('fec0::/10') + + @property + def is_private(self): + """Test if this address is allocated for private networks. + + Returns: + A boolean, True if the address is reserved per RFC 4193. + + """ + return self in IPv6Network('fc00::/7') + + @property + def ipv4_mapped(self): + """Return the IPv4 mapped address. + + Returns: + If the IPv6 address is a v4 mapped address, return the + IPv4 mapped address. Return None otherwise. + + """ + if (self._ip >> 32) != 0xFFFF: + return None + return IPv4Address(self._ip & 0xFFFFFFFF) + + @property + def teredo(self): + """Tuple of embedded teredo IPs. + + Returns: + Tuple of the (server, client) IPs or None if the address + doesn't appear to be a teredo address (doesn't start with + 2001::/32) + + """ + if (self._ip >> 96) != 0x20010000: + return None + return (IPv4Address((self._ip >> 64) & 0xFFFFFFFF), + IPv4Address(~self._ip & 0xFFFFFFFF)) + + @property + def sixtofour(self): + """Return the IPv4 6to4 embedded address. + + Returns: + The IPv4 6to4-embedded address if present or None if the + address doesn't appear to contain a 6to4 embedded address. + + """ + if (self._ip >> 112) != 0x2002: + return None + return IPv4Address((self._ip >> 80) & 0xFFFFFFFF) + + +class IPv6Address(_BaseV6, _BaseIP): + + """Represent and manipulate single IPv6 Addresses. + """ + + def __init__(self, address): + """Instantiate a new IPv6 address object. + + Args: + address: A string or integer representing the IP + + Additionally, an integer can be passed, so + IPv6Address('2001:4860::') == + IPv6Address(42541956101370907050197289607612071936L). + or, more generally + IPv6Address(IPv6Address('2001:4860::')._ip) == + IPv6Address('2001:4860::') + + Raises: + AddressValueError: If address isn't a valid IPv6 address. + + """ + _BaseIP.__init__(self, address) + _BaseV6.__init__(self, address) + + # Efficient constructor from integer. + if isinstance(address, (int, long)): + self._ip = address + if address < 0 or address > self._ALL_ONES: + raise AddressValueError(address) + return + + # Constructing from a packed address + if _compat_has_real_bytes: + if isinstance(address, bytes) and len(address) == 16: + tmp = struct.unpack('!QQ', address) + self._ip = (tmp[0] << 64) | tmp[1] + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP string. + addr_str = str(address) + if not addr_str: + raise AddressValueError('') + + self._ip = self._ip_int_from_string(addr_str) + + +class IPv6Network(_BaseV6, _BaseNet): + + """This class represents and manipulates 128-bit IPv6 networks. + + Attributes: [examples for IPv6('2001:658:22A:CAFE:200::1/64')] + .ip: IPv6Address('2001:658:22a:cafe:200::1') + .network: IPv6Address('2001:658:22a:cafe::') + .hostmask: IPv6Address('::ffff:ffff:ffff:ffff') + .broadcast: IPv6Address('2001:658:22a:cafe:ffff:ffff:ffff:ffff') + .netmask: IPv6Address('ffff:ffff:ffff:ffff::') + .prefixlen: 64 + + """ + + + def __init__(self, address, strict=False): + """Instantiate a new IPv6 Network object. + + Args: + address: A string or integer representing the IPv6 network or the IP + and prefix/netmask. + '2001:4860::/128' + '2001:4860:0000:0000:0000:0000:0000:0000/128' + '2001:4860::' + are all functionally the same in IPv6. That is to say, + failing to provide a subnetmask will create an object with + a mask of /128. + + Additionally, an integer can be passed, so + IPv6Network('2001:4860::') == + IPv6Network(42541956101370907050197289607612071936L). + or, more generally + IPv6Network(IPv6Network('2001:4860::')._ip) == + IPv6Network('2001:4860::') + + strict: A boolean. If true, ensure that we have been passed + A true network address, eg, 192.168.1.0/24 and not an + IP address on a network, eg, 192.168.1.1/24. + + Raises: + AddressValueError: If address isn't a valid IPv6 address. + NetmaskValueError: If the netmask isn't valid for + an IPv6 address. + ValueError: If strict was True and a network address was not + supplied. + + """ + _BaseNet.__init__(self, address) + _BaseV6.__init__(self, address) + + # Efficient constructor from integer. + if isinstance(address, (int, long)): + self._ip = address + self.ip = IPv6Address(self._ip) + self._prefixlen = self._max_prefixlen + self.netmask = IPv6Address(self._ALL_ONES) + if address < 0 or address > self._ALL_ONES: + raise AddressValueError(address) + return + + # Constructing from a packed address + if _compat_has_real_bytes: + if isinstance(address, bytes) and len(address) == 16: + tmp = struct.unpack('!QQ', address) + self._ip = (tmp[0] << 64) | tmp[1] + self.ip = IPv6Address(self._ip) + self._prefixlen = self._max_prefixlen + self.netmask = IPv6Address(self._ALL_ONES) + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP prefix string. + addr = str(address).split('/') + + if len(addr) > 2: + raise AddressValueError(address) + + self._ip = self._ip_int_from_string(addr[0]) + self.ip = IPv6Address(self._ip) + + if len(addr) == 2: + if self._is_valid_netmask(addr[1]): + self._prefixlen = int(addr[1]) + else: + raise NetmaskValueError(addr[1]) + else: + self._prefixlen = self._max_prefixlen + + self.netmask = IPv6Address(self._ip_int_from_prefix(self._prefixlen)) + + if strict: + if self.ip != self.network: + raise ValueError('%s has host bits set' % + self.ip) + + def _is_valid_netmask(self, prefixlen): + """Verify that the netmask/prefixlen is valid. + + Args: + prefixlen: A string, the netmask in prefix length format. + + Returns: + A boolean, True if the prefix represents a valid IPv6 + netmask. + + """ + try: + prefixlen = int(prefixlen) + except ValueError: + return False + return 0 <= prefixlen <= self._max_prefixlen + + @property + def with_netmask(self): + return self.with_prefixlen -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed Nov 9 01:38:09 2011 From: jython-checkins at python.org (alan.kennedy) Date: Wed, 09 Nov 2011 01:38:09 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=282=2E5=29=3A_Adding_Google_ip?= =?utf8?q?addr=2Epy=2C_from_revision_234=2E?= Message-ID: http://hg.python.org/jython/rev/49fb61a07b8d changeset: 6276:49fb61a07b8d branch: 2.5 parent: 6270:cc548bacc001 user: Alan Kennedy date: Wed Nov 09 00:24:53 2011 +0000 summary: Adding Google ipaddr.py, from revision 234. files: Lib/_google_ipaddr_r234.py | 1907 ++++++++++++++++++++++++ 1 files changed, 1907 insertions(+), 0 deletions(-) diff --git a/Lib/_google_ipaddr_r234.py b/Lib/_google_ipaddr_r234.py new file mode 100644 --- /dev/null +++ b/Lib/_google_ipaddr_r234.py @@ -0,0 +1,1907 @@ +#!/usr/bin/python +# +# Copyright 2007 Google Inc. +# Licensed to PSF under a Contributor Agreement. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +"""A fast, lightweight IPv4/IPv6 manipulation library in Python. + +This library is used to create/poke/manipulate IPv4 and IPv6 addresses +and networks. + +""" + +__version__ = 'trunk' + +import struct + +IPV4LENGTH = 32 +IPV6LENGTH = 128 + + +class AddressValueError(ValueError): + """A Value Error related to the address.""" + + +class NetmaskValueError(ValueError): + """A Value Error related to the netmask.""" + + +def IPAddress(address, version=None): + """Take an IP string/int and return an object of the correct type. + + Args: + address: A string or integer, the IP address. Either IPv4 or + IPv6 addresses may be supplied; integers less than 2**32 will + be considered to be IPv4 by default. + version: An Integer, 4 or 6. If set, don't try to automatically + determine what the IP address type is. important for things + like IPAddress(1), which could be IPv4, '0.0.0.1', or IPv6, + '::1'. + + Returns: + An IPv4Address or IPv6Address object. + + Raises: + ValueError: if the string passed isn't either a v4 or a v6 + address. + + """ + if version: + if version == 4: + return IPv4Address(address) + elif version == 6: + return IPv6Address(address) + + try: + return IPv4Address(address) + except (AddressValueError, NetmaskValueError): + pass + + try: + return IPv6Address(address) + except (AddressValueError, NetmaskValueError): + pass + + raise ValueError('%r does not appear to be an IPv4 or IPv6 address' % + address) + + +def IPNetwork(address, version=None, strict=False): + """Take an IP string/int and return an object of the correct type. + + Args: + address: A string or integer, the IP address. Either IPv4 or + IPv6 addresses may be supplied; integers less than 2**32 will + be considered to be IPv4 by default. + version: An Integer, if set, don't try to automatically + determine what the IP address type is. important for things + like IPNetwork(1), which could be IPv4, '0.0.0.1/32', or IPv6, + '::1/128'. + + Returns: + An IPv4Network or IPv6Network object. + + Raises: + ValueError: if the string passed isn't either a v4 or a v6 + address. Or if a strict network was requested and a strict + network wasn't given. + + """ + if version: + if version == 4: + return IPv4Network(address, strict) + elif version == 6: + return IPv6Network(address, strict) + + try: + return IPv4Network(address, strict) + except (AddressValueError, NetmaskValueError): + pass + + try: + return IPv6Network(address, strict) + except (AddressValueError, NetmaskValueError): + pass + + raise ValueError('%r does not appear to be an IPv4 or IPv6 network' % + address) + + +def v4_int_to_packed(address): + """The binary representation of this address. + + Args: + address: An integer representation of an IPv4 IP address. + + Returns: + The binary representation of this address. + + Raises: + ValueError: If the integer is too large to be an IPv4 IP + address. + """ + if address > _BaseV4._ALL_ONES: + raise ValueError('Address too large for IPv4') + return struct.pack('!I', address) + + +def v6_int_to_packed(address): + """The binary representation of this address. + + Args: + address: An integer representation of an IPv4 IP address. + + Returns: + The binary representation of this address. + """ + return struct.pack('!QQ', address >> 64, address & (2**64 - 1)) + + +def _find_address_range(addresses): + """Find a sequence of addresses. + + Args: + addresses: a list of IPv4 or IPv6 addresses. + + Returns: + A tuple containing the first and last IP addresses in the sequence. + + """ + first = last = addresses[0] + for ip in addresses[1:]: + if ip._ip == last._ip + 1: + last = ip + else: + break + return (first, last) + +def _get_prefix_length(number1, number2, bits): + """Get the number of leading bits that are same for two numbers. + + Args: + number1: an integer. + number2: another integer. + bits: the maximum number of bits to compare. + + Returns: + The number of leading bits that are the same for two numbers. + + """ + for i in range(bits): + if number1 >> i == number2 >> i: + return bits - i + return 0 + +def _count_righthand_zero_bits(number, bits): + """Count the number of zero bits on the right hand side. + + Args: + number: an integer. + bits: maximum number of bits to count. + + Returns: + The number of zero bits on the right hand side of the number. + + """ + if number == 0: + return bits + for i in range(bits): + if (number >> i) % 2: + return i + +def summarize_address_range(first, last): + """Summarize a network range given the first and last IP addresses. + + Example: + >>> summarize_address_range(IPv4Address('1.1.1.0'), + IPv4Address('1.1.1.130')) + [IPv4Network('1.1.1.0/25'), IPv4Network('1.1.1.128/31'), + IPv4Network('1.1.1.130/32')] + + Args: + first: the first IPv4Address or IPv6Address in the range. + last: the last IPv4Address or IPv6Address in the range. + + Returns: + The address range collapsed to a list of IPv4Network's or + IPv6Network's. + + Raise: + TypeError: + If the first and last objects are not IP addresses. + If the first and last objects are not the same version. + ValueError: + If the last object is not greater than the first. + If the version is not 4 or 6. + + """ + if not (isinstance(first, _BaseIP) and isinstance(last, _BaseIP)): + raise TypeError('first and last must be IP addresses, not networks') + if first.version != last.version: + raise TypeError("%s and %s are not of the same version" % ( + str(first), str(last))) + if first > last: + raise ValueError('last IP address must be greater than first') + + networks = [] + + if first.version == 4: + ip = IPv4Network + elif first.version == 6: + ip = IPv6Network + else: + raise ValueError('unknown IP version') + + ip_bits = first._max_prefixlen + first_int = first._ip + last_int = last._ip + while first_int <= last_int: + nbits = _count_righthand_zero_bits(first_int, ip_bits) + current = None + while nbits >= 0: + addend = 2**nbits - 1 + current = first_int + addend + nbits -= 1 + if current <= last_int: + break + prefix = _get_prefix_length(first_int, current, ip_bits) + net = ip('%s/%d' % (str(first), prefix)) + networks.append(net) + if current == ip._ALL_ONES: + break + first_int = current + 1 + first = IPAddress(first_int, version=first._version) + return networks + +def _collapse_address_list_recursive(addresses): + """Loops through the addresses, collapsing concurrent netblocks. + + Example: + + ip1 = IPv4Network('1.1.0.0/24') + ip2 = IPv4Network('1.1.1.0/24') + ip3 = IPv4Network('1.1.2.0/24') + ip4 = IPv4Network('1.1.3.0/24') + ip5 = IPv4Network('1.1.4.0/24') + ip6 = IPv4Network('1.1.0.1/22') + + _collapse_address_list_recursive([ip1, ip2, ip3, ip4, ip5, ip6]) -> + [IPv4Network('1.1.0.0/22'), IPv4Network('1.1.4.0/24')] + + This shouldn't be called directly; it is called via + collapse_address_list([]). + + Args: + addresses: A list of IPv4Network's or IPv6Network's + + Returns: + A list of IPv4Network's or IPv6Network's depending on what we were + passed. + + """ + ret_array = [] + optimized = False + + for cur_addr in addresses: + if not ret_array: + ret_array.append(cur_addr) + continue + if cur_addr in ret_array[-1]: + optimized = True + elif cur_addr == ret_array[-1].supernet().subnet()[1]: + ret_array.append(ret_array.pop().supernet()) + optimized = True + else: + ret_array.append(cur_addr) + + if optimized: + return _collapse_address_list_recursive(ret_array) + + return ret_array + + +def collapse_address_list(addresses): + """Collapse a list of IP objects. + + Example: + collapse_address_list([IPv4('1.1.0.0/24'), IPv4('1.1.1.0/24')]) -> + [IPv4('1.1.0.0/23')] + + Args: + addresses: A list of IPv4Network or IPv6Network objects. + + Returns: + A list of IPv4Network or IPv6Network objects depending on what we + were passed. + + Raises: + TypeError: If passed a list of mixed version objects. + + """ + i = 0 + addrs = [] + ips = [] + nets = [] + + # split IP addresses and networks + for ip in addresses: + if isinstance(ip, _BaseIP): + if ips and ips[-1]._version != ip._version: + raise TypeError("%s and %s are not of the same version" % ( + str(ip), str(ips[-1]))) + ips.append(ip) + elif ip._prefixlen == ip._max_prefixlen: + if ips and ips[-1]._version != ip._version: + raise TypeError("%s and %s are not of the same version" % ( + str(ip), str(ips[-1]))) + ips.append(ip.ip) + else: + if nets and nets[-1]._version != ip._version: + raise TypeError("%s and %s are not of the same version" % ( + str(ip), str(ips[-1]))) + nets.append(ip) + + # sort and dedup + ips = sorted(set(ips)) + nets = sorted(set(nets)) + + while i < len(ips): + (first, last) = _find_address_range(ips[i:]) + i = ips.index(last) + 1 + addrs.extend(summarize_address_range(first, last)) + + return _collapse_address_list_recursive(sorted( + addrs + nets, key=_BaseNet._get_networks_key)) + +# backwards compatibility +CollapseAddrList = collapse_address_list + +# Test whether this Python implementation supports byte objects that +# are not identical to str ones. +# We need to exclude platforms where bytes == str so that we can +# distinguish between packed representations and strings, for example +# b'12::' (the IPv4 address 49.50.58.58) and '12::' (an IPv6 address). +try: + _compat_has_real_bytes = bytes is not str +except NameError: # other._ip + return False + + # Shorthand for Integer addition and subtraction. This is not + # meant to ever support addition/subtraction of addresses. + def __add__(self, other): + if not isinstance(other, int): + return NotImplemented + return IPAddress(int(self) + other, version=self._version) + + def __sub__(self, other): + if not isinstance(other, int): + return NotImplemented + return IPAddress(int(self) - other, version=self._version) + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, str(self)) + + def __str__(self): + return '%s' % self._string_from_ip_int(self._ip) + + def __hash__(self): + return hash(hex(long(self._ip))) + + def _get_address_key(self): + return (self._version, self) + + @property + def version(self): + raise NotImplementedError('BaseIP has no version') + + +class _BaseNet(_IPAddrBase): + + """A generic IP object. + + This IP class contains the version independent methods which are + used by networks. + + """ + + def __init__(self, address): + self._cache = {} + + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, str(self)) + + def iterhosts(self): + """Generate Iterator over usable hosts in a network. + + This is like __iter__ except it doesn't return the network + or broadcast addresses. + + """ + cur = int(self.network) + 1 + bcast = int(self.broadcast) - 1 + while cur <= bcast: + cur += 1 + yield IPAddress(cur - 1, version=self._version) + + def __iter__(self): + cur = int(self.network) + bcast = int(self.broadcast) + while cur <= bcast: + cur += 1 + yield IPAddress(cur - 1, version=self._version) + + def __getitem__(self, n): + network = int(self.network) + broadcast = int(self.broadcast) + if n >= 0: + if network + n > broadcast: + raise IndexError + return IPAddress(network + n, version=self._version) + else: + n += 1 + if broadcast + n < network: + raise IndexError + return IPAddress(broadcast + n, version=self._version) + + def __lt__(self, other): + if self._version != other._version: + raise TypeError('%s and %s are not of the same version' % ( + str(self), str(other))) + if not isinstance(other, _BaseNet): + raise TypeError('%s and %s are not of the same type' % ( + str(self), str(other))) + if self.network != other.network: + return self.network < other.network + if self.netmask != other.netmask: + return self.netmask < other.netmask + return False + + def __gt__(self, other): + if self._version != other._version: + raise TypeError('%s and %s are not of the same version' % ( + str(self), str(other))) + if not isinstance(other, _BaseNet): + raise TypeError('%s and %s are not of the same type' % ( + str(self), str(other))) + if self.network != other.network: + return self.network > other.network + if self.netmask != other.netmask: + return self.netmask > other.netmask + return False + + def __le__(self, other): + gt = self.__gt__(other) + if gt is NotImplemented: + return NotImplemented + return not gt + + def __ge__(self, other): + lt = self.__lt__(other) + if lt is NotImplemented: + return NotImplemented + return not lt + + def __eq__(self, other): + try: + return (self._version == other._version + and self.network == other.network + and int(self.netmask) == int(other.netmask)) + except AttributeError: + if isinstance(other, _BaseIP): + return (self._version == other._version + and self._ip == other._ip) + + def __ne__(self, other): + eq = self.__eq__(other) + if eq is NotImplemented: + return NotImplemented + return not eq + + def __str__(self): + return '%s/%s' % (str(self.ip), + str(self._prefixlen)) + + def __hash__(self): + return hash(int(self.network) ^ int(self.netmask)) + + def __contains__(self, other): + # always false if one is v4 and the other is v6. + if self._version != other._version: + return False + # dealing with another network. + if isinstance(other, _BaseNet): + return (self.network <= other.network and + self.broadcast >= other.broadcast) + # dealing with another address + else: + return (int(self.network) <= int(other._ip) <= + int(self.broadcast)) + + def overlaps(self, other): + """Tell if self is partly contained in other.""" + return self.network in other or self.broadcast in other or ( + other.network in self or other.broadcast in self) + + @property + def network(self): + x = self._cache.get('network') + if x is None: + x = IPAddress(self._ip & int(self.netmask), version=self._version) + self._cache['network'] = x + return x + + @property + def broadcast(self): + x = self._cache.get('broadcast') + if x is None: + x = IPAddress(self._ip | int(self.hostmask), version=self._version) + self._cache['broadcast'] = x + return x + + @property + def hostmask(self): + x = self._cache.get('hostmask') + if x is None: + x = IPAddress(int(self.netmask) ^ self._ALL_ONES, + version=self._version) + self._cache['hostmask'] = x + return x + + @property + def with_prefixlen(self): + return '%s/%d' % (str(self.ip), self._prefixlen) + + @property + def with_netmask(self): + return '%s/%s' % (str(self.ip), str(self.netmask)) + + @property + def with_hostmask(self): + return '%s/%s' % (str(self.ip), str(self.hostmask)) + + @property + def numhosts(self): + """Number of hosts in the current subnet.""" + return int(self.broadcast) - int(self.network) + 1 + + @property + def version(self): + raise NotImplementedError('BaseNet has no version') + + @property + def prefixlen(self): + return self._prefixlen + + def address_exclude(self, other): + """Remove an address from a larger block. + + For example: + + addr1 = IPNetwork('10.1.1.0/24') + addr2 = IPNetwork('10.1.1.0/26') + addr1.address_exclude(addr2) = + [IPNetwork('10.1.1.64/26'), IPNetwork('10.1.1.128/25')] + + or IPv6: + + addr1 = IPNetwork('::1/32') + addr2 = IPNetwork('::1/128') + addr1.address_exclude(addr2) = [IPNetwork('::0/128'), + IPNetwork('::2/127'), + IPNetwork('::4/126'), + IPNetwork('::8/125'), + ... + IPNetwork('0:0:8000::/33')] + + Args: + other: An IPvXNetwork object of the same type. + + Returns: + A sorted list of IPvXNetwork objects addresses which is self + minus other. + + Raises: + TypeError: If self and other are of difffering address + versions, or if other is not a network object. + ValueError: If other is not completely contained by self. + + """ + if not self._version == other._version: + raise TypeError("%s and %s are not of the same version" % ( + str(self), str(other))) + + if not isinstance(other, _BaseNet): + raise TypeError("%s is not a network object" % str(other)) + + if other not in self: + raise ValueError('%s not contained in %s' % (str(other), + str(self))) + if other == self: + return [] + + ret_addrs = [] + + # Make sure we're comparing the network of other. + other = IPNetwork('%s/%s' % (str(other.network), str(other.prefixlen)), + version=other._version) + + s1, s2 = self.subnet() + while s1 != other and s2 != other: + if other in s1: + ret_addrs.append(s2) + s1, s2 = s1.subnet() + elif other in s2: + ret_addrs.append(s1) + s1, s2 = s2.subnet() + else: + # If we got here, there's a bug somewhere. + assert True == False, ('Error performing exclusion: ' + 's1: %s s2: %s other: %s' % + (str(s1), str(s2), str(other))) + if s1 == other: + ret_addrs.append(s2) + elif s2 == other: + ret_addrs.append(s1) + else: + # If we got here, there's a bug somewhere. + assert True == False, ('Error performing exclusion: ' + 's1: %s s2: %s other: %s' % + (str(s1), str(s2), str(other))) + + return sorted(ret_addrs, key=_BaseNet._get_networks_key) + + def compare_networks(self, other): + """Compare two IP objects. + + This is only concerned about the comparison of the integer + representation of the network addresses. This means that the + host bits aren't considered at all in this method. If you want + to compare host bits, you can easily enough do a + 'HostA._ip < HostB._ip' + + Args: + other: An IP object. + + Returns: + If the IP versions of self and other are the same, returns: + + -1 if self < other: + eg: IPv4('1.1.1.0/24') < IPv4('1.1.2.0/24') + IPv6('1080::200C:417A') < IPv6('1080::200B:417B') + 0 if self == other + eg: IPv4('1.1.1.1/24') == IPv4('1.1.1.2/24') + IPv6('1080::200C:417A/96') == IPv6('1080::200C:417B/96') + 1 if self > other + eg: IPv4('1.1.1.0/24') > IPv4('1.1.0.0/24') + IPv6('1080::1:200C:417A/112') > + IPv6('1080::0:200C:417A/112') + + If the IP versions of self and other are different, returns: + + -1 if self._version < other._version + eg: IPv4('10.0.0.1/24') < IPv6('::1/128') + 1 if self._version > other._version + eg: IPv6('::1/128') > IPv4('255.255.255.0/24') + + """ + if self._version < other._version: + return -1 + if self._version > other._version: + return 1 + # self._version == other._version below here: + if self.network < other.network: + return -1 + if self.network > other.network: + return 1 + # self.network == other.network below here: + if self.netmask < other.netmask: + return -1 + if self.netmask > other.netmask: + return 1 + # self.network == other.network and self.netmask == other.netmask + return 0 + + def _get_networks_key(self): + """Network-only key function. + + Returns an object that identifies this address' network and + netmask. This function is a suitable "key" argument for sorted() + and list.sort(). + + """ + return (self._version, self.network, self.netmask) + + def _ip_int_from_prefix(self, prefixlen=None): + """Turn the prefix length netmask into a int for comparison. + + Args: + prefixlen: An integer, the prefix length. + + Returns: + An integer. + + """ + if not prefixlen and prefixlen != 0: + prefixlen = self._prefixlen + return self._ALL_ONES ^ (self._ALL_ONES >> prefixlen) + + def _prefix_from_ip_int(self, ip_int, mask=32): + """Return prefix length from the decimal netmask. + + Args: + ip_int: An integer, the IP address. + mask: The netmask. Defaults to 32. + + Returns: + An integer, the prefix length. + + """ + while mask: + if ip_int & 1 == 1: + break + ip_int >>= 1 + mask -= 1 + + return mask + + def _ip_string_from_prefix(self, prefixlen=None): + """Turn a prefix length into a dotted decimal string. + + Args: + prefixlen: An integer, the netmask prefix length. + + Returns: + A string, the dotted decimal netmask string. + + """ + if not prefixlen: + prefixlen = self._prefixlen + return self._string_from_ip_int(self._ip_int_from_prefix(prefixlen)) + + def iter_subnets(self, prefixlen_diff=1, new_prefix=None): + """The subnets which join to make the current subnet. + + In the case that self contains only one IP + (self._prefixlen == 32 for IPv4 or self._prefixlen == 128 + for IPv6), return a list with just ourself. + + Args: + prefixlen_diff: An integer, the amount the prefix length + should be increased by. This should not be set if + new_prefix is also set. + new_prefix: The desired new prefix length. This must be a + larger number (smaller prefix) than the existing prefix. + This should not be set if prefixlen_diff is also set. + + Returns: + An iterator of IPv(4|6) objects. + + Raises: + ValueError: The prefixlen_diff is too small or too large. + OR + prefixlen_diff and new_prefix are both set or new_prefix + is a smaller number than the current prefix (smaller + number means a larger network) + + """ + if self._prefixlen == self._max_prefixlen: + yield self + return + + if new_prefix is not None: + if new_prefix < self._prefixlen: + raise ValueError('new prefix must be longer') + if prefixlen_diff != 1: + raise ValueError('cannot set prefixlen_diff and new_prefix') + prefixlen_diff = new_prefix - self._prefixlen + + if prefixlen_diff < 0: + raise ValueError('prefix length diff must be > 0') + new_prefixlen = self._prefixlen + prefixlen_diff + + if not self._is_valid_netmask(str(new_prefixlen)): + raise ValueError( + 'prefix length diff %d is invalid for netblock %s' % ( + new_prefixlen, str(self))) + + first = IPNetwork('%s/%s' % (str(self.network), + str(self._prefixlen + prefixlen_diff)), + version=self._version) + + yield first + current = first + while True: + broadcast = current.broadcast + if broadcast == self.broadcast: + return + new_addr = IPAddress(int(broadcast) + 1, version=self._version) + current = IPNetwork('%s/%s' % (str(new_addr), str(new_prefixlen)), + version=self._version) + + yield current + + def masked(self): + """Return the network object with the host bits masked out.""" + return IPNetwork('%s/%d' % (self.network, self._prefixlen), + version=self._version) + + def subnet(self, prefixlen_diff=1, new_prefix=None): + """Return a list of subnets, rather than an iterator.""" + return list(self.iter_subnets(prefixlen_diff, new_prefix)) + + def supernet(self, prefixlen_diff=1, new_prefix=None): + """The supernet containing the current network. + + Args: + prefixlen_diff: An integer, the amount the prefix length of + the network should be decreased by. For example, given a + /24 network and a prefixlen_diff of 3, a supernet with a + /21 netmask is returned. + + Returns: + An IPv4 network object. + + Raises: + ValueError: If self.prefixlen - prefixlen_diff < 0. I.e., you have a + negative prefix length. + OR + If prefixlen_diff and new_prefix are both set or new_prefix is a + larger number than the current prefix (larger number means a + smaller network) + + """ + if self._prefixlen == 0: + return self + + if new_prefix is not None: + if new_prefix > self._prefixlen: + raise ValueError('new prefix must be shorter') + if prefixlen_diff != 1: + raise ValueError('cannot set prefixlen_diff and new_prefix') + prefixlen_diff = self._prefixlen - new_prefix + + + if self.prefixlen - prefixlen_diff < 0: + raise ValueError( + 'current prefixlen is %d, cannot have a prefixlen_diff of %d' % + (self.prefixlen, prefixlen_diff)) + return IPNetwork('%s/%s' % (str(self.network), + str(self.prefixlen - prefixlen_diff)), + version=self._version) + + # backwards compatibility + Subnet = subnet + Supernet = supernet + AddressExclude = address_exclude + CompareNetworks = compare_networks + Contains = __contains__ + + +class _BaseV4(object): + + """Base IPv4 object. + + The following methods are used by IPv4 objects in both single IP + addresses and networks. + + """ + + # Equivalent to 255.255.255.255 or 32 bits of 1's. + _ALL_ONES = (2**IPV4LENGTH) - 1 + _DECIMAL_DIGITS = frozenset('0123456789') + + def __init__(self, address): + self._version = 4 + self._max_prefixlen = IPV4LENGTH + + def _explode_shorthand_ip_string(self, ip_str=None): + if not ip_str: + ip_str = str(self) + return ip_str + + def _ip_int_from_string(self, ip_str): + """Turn the given IP string into an integer for comparison. + + Args: + ip_str: A string, the IP ip_str. + + Returns: + The IP ip_str as an integer. + + Raises: + AddressValueError: if ip_str isn't a valid IPv4 Address. + + """ + octets = ip_str.split('.') + if len(octets) != 4: + raise AddressValueError(ip_str) + + packed_ip = 0 + for oc in octets: + try: + packed_ip = (packed_ip << 8) | self._parse_octet(oc) + except ValueError: + raise AddressValueError(ip_str) + return packed_ip + + def _parse_octet(self, octet_str): + """Convert a decimal octet into an integer. + + Args: + octet_str: A string, the number to parse. + + Returns: + The octet as an integer. + + Raises: + ValueError: if the octet isn't strictly a decimal from [0..255]. + + """ + # Whitelist the characters, since int() allows a lot of bizarre stuff. + if not self._DECIMAL_DIGITS.issuperset(octet_str): + raise ValueError + octet_int = int(octet_str, 10) + # Disallow leading zeroes, because no clear standard exists on + # whether these should be interpreted as decimal or octal. + if octet_int > 255 or (octet_str[0] == '0' and len(octet_str) > 1): + raise ValueError + return octet_int + + def _string_from_ip_int(self, ip_int): + """Turns a 32-bit integer into dotted decimal notation. + + Args: + ip_int: An integer, the IP address. + + Returns: + The IP address as a string in dotted decimal notation. + + """ + octets = [] + for _ in xrange(4): + octets.insert(0, str(ip_int & 0xFF)) + ip_int >>= 8 + return '.'.join(octets) + + @property + def max_prefixlen(self): + return self._max_prefixlen + + @property + def packed(self): + """The binary representation of this address.""" + return v4_int_to_packed(self._ip) + + @property + def version(self): + return self._version + + @property + def is_reserved(self): + """Test if the address is otherwise IETF reserved. + + Returns: + A boolean, True if the address is within the + reserved IPv4 Network range. + + """ + return self in IPv4Network('240.0.0.0/4') + + @property + def is_private(self): + """Test if this address is allocated for private networks. + + Returns: + A boolean, True if the address is reserved per RFC 1918. + + """ + return (self in IPv4Network('10.0.0.0/8') or + self in IPv4Network('172.16.0.0/12') or + self in IPv4Network('192.168.0.0/16')) + + @property + def is_multicast(self): + """Test if the address is reserved for multicast use. + + Returns: + A boolean, True if the address is multicast. + See RFC 3171 for details. + + """ + return self in IPv4Network('224.0.0.0/4') + + @property + def is_unspecified(self): + """Test if the address is unspecified. + + Returns: + A boolean, True if this is the unspecified address as defined in + RFC 5735 3. + + """ + return self in IPv4Network('0.0.0.0') + + @property + def is_loopback(self): + """Test if the address is a loopback address. + + Returns: + A boolean, True if the address is a loopback per RFC 3330. + + """ + return self in IPv4Network('127.0.0.0/8') + + @property + def is_link_local(self): + """Test if the address is reserved for link-local. + + Returns: + A boolean, True if the address is link-local per RFC 3927. + + """ + return self in IPv4Network('169.254.0.0/16') + + +class IPv4Address(_BaseV4, _BaseIP): + + """Represent and manipulate single IPv4 Addresses.""" + + def __init__(self, address): + + """ + Args: + address: A string or integer representing the IP + '192.168.1.1' + + Additionally, an integer can be passed, so + IPv4Address('192.168.1.1') == IPv4Address(3232235777). + or, more generally + IPv4Address(int(IPv4Address('192.168.1.1'))) == + IPv4Address('192.168.1.1') + + Raises: + AddressValueError: If ipaddr isn't a valid IPv4 address. + + """ + _BaseIP.__init__(self, address) + _BaseV4.__init__(self, address) + + # Efficient constructor from integer. + if isinstance(address, (int, long)): + self._ip = address + if address < 0 or address > self._ALL_ONES: + raise AddressValueError(address) + return + + # Constructing from a packed address + if _compat_has_real_bytes: + if isinstance(address, bytes) and len(address) == 4: + self._ip = struct.unpack('!I', address)[0] + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP string. + addr_str = str(address) + self._ip = self._ip_int_from_string(addr_str) + + +class IPv4Network(_BaseV4, _BaseNet): + + """This class represents and manipulates 32-bit IPv4 networks. + + Attributes: [examples for IPv4Network('1.2.3.4/27')] + ._ip: 16909060 + .ip: IPv4Address('1.2.3.4') + .network: IPv4Address('1.2.3.0') + .hostmask: IPv4Address('0.0.0.31') + .broadcast: IPv4Address('1.2.3.31') + .netmask: IPv4Address('255.255.255.224') + .prefixlen: 27 + + """ + + # the valid octets for host and netmasks. only useful for IPv4. + _valid_mask_octets = set((255, 254, 252, 248, 240, 224, 192, 128, 0)) + + def __init__(self, address, strict=False): + """Instantiate a new IPv4 network object. + + Args: + address: A string or integer representing the IP [& network]. + '192.168.1.1/24' + '192.168.1.1/255.255.255.0' + '192.168.1.1/0.0.0.255' + are all functionally the same in IPv4. Similarly, + '192.168.1.1' + '192.168.1.1/255.255.255.255' + '192.168.1.1/32' + are also functionaly equivalent. That is to say, failing to + provide a subnetmask will create an object with a mask of /32. + + If the mask (portion after the / in the argument) is given in + dotted quad form, it is treated as a netmask if it starts with a + non-zero field (e.g. /255.0.0.0 == /8) and as a hostmask if it + starts with a zero field (e.g. 0.255.255.255 == /8), with the + single exception of an all-zero mask which is treated as a + netmask == /0. If no mask is given, a default of /32 is used. + + Additionally, an integer can be passed, so + IPv4Network('192.168.1.1') == IPv4Network(3232235777). + or, more generally + IPv4Network(int(IPv4Network('192.168.1.1'))) == + IPv4Network('192.168.1.1') + + strict: A boolean. If true, ensure that we have been passed + A true network address, eg, 192.168.1.0/24 and not an + IP address on a network, eg, 192.168.1.1/24. + + Raises: + AddressValueError: If ipaddr isn't a valid IPv4 address. + NetmaskValueError: If the netmask isn't valid for + an IPv4 address. + ValueError: If strict was True and a network address was not + supplied. + + """ + _BaseNet.__init__(self, address) + _BaseV4.__init__(self, address) + + # Efficient constructor from integer. + if isinstance(address, (int, long)): + self._ip = address + self.ip = IPv4Address(self._ip) + self._prefixlen = self._max_prefixlen + self.netmask = IPv4Address(self._ALL_ONES) + if address < 0 or address > self._ALL_ONES: + raise AddressValueError(address) + return + + # Constructing from a packed address + if _compat_has_real_bytes: + if isinstance(address, bytes) and len(address) == 4: + self._ip = struct.unpack('!I', address)[0] + self.ip = IPv4Address(self._ip) + self._prefixlen = self._max_prefixlen + self.netmask = IPv4Address(self._ALL_ONES) + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP prefix string. + addr = str(address).split('/') + + if len(addr) > 2: + raise AddressValueError(address) + + self._ip = self._ip_int_from_string(addr[0]) + self.ip = IPv4Address(self._ip) + + if len(addr) == 2: + mask = addr[1].split('.') + if len(mask) == 4: + # We have dotted decimal netmask. + if self._is_valid_netmask(addr[1]): + self.netmask = IPv4Address(self._ip_int_from_string( + addr[1])) + elif self._is_hostmask(addr[1]): + self.netmask = IPv4Address( + self._ip_int_from_string(addr[1]) ^ self._ALL_ONES) + else: + raise NetmaskValueError('%s is not a valid netmask' + % addr[1]) + + self._prefixlen = self._prefix_from_ip_int(int(self.netmask)) + else: + # We have a netmask in prefix length form. + if not self._is_valid_netmask(addr[1]): + raise NetmaskValueError(addr[1]) + self._prefixlen = int(addr[1]) + self.netmask = IPv4Address(self._ip_int_from_prefix( + self._prefixlen)) + else: + self._prefixlen = self._max_prefixlen + self.netmask = IPv4Address(self._ip_int_from_prefix( + self._prefixlen)) + if strict: + if self.ip != self.network: + raise ValueError('%s has host bits set' % + self.ip) + + def _is_hostmask(self, ip_str): + """Test if the IP string is a hostmask (rather than a netmask). + + Args: + ip_str: A string, the potential hostmask. + + Returns: + A boolean, True if the IP string is a hostmask. + + """ + bits = ip_str.split('.') + try: + parts = [int(x) for x in bits if int(x) in self._valid_mask_octets] + except ValueError: + return False + if len(parts) != len(bits): + return False + if parts[0] < parts[-1]: + return True + return False + + def _is_valid_netmask(self, netmask): + """Verify that the netmask is valid. + + Args: + netmask: A string, either a prefix or dotted decimal + netmask. + + Returns: + A boolean, True if the prefix represents a valid IPv4 + netmask. + + """ + mask = netmask.split('.') + if len(mask) == 4: + if [x for x in mask if int(x) not in self._valid_mask_octets]: + return False + if [y for idx, y in enumerate(mask) if idx > 0 and + y > mask[idx - 1]]: + return False + return True + try: + netmask = int(netmask) + except ValueError: + return False + return 0 <= netmask <= self._max_prefixlen + + # backwards compatibility + IsRFC1918 = lambda self: self.is_private + IsMulticast = lambda self: self.is_multicast + IsLoopback = lambda self: self.is_loopback + IsLinkLocal = lambda self: self.is_link_local + + +class _BaseV6(object): + + """Base IPv6 object. + + The following methods are used by IPv6 objects in both single IP + addresses and networks. + + """ + + _ALL_ONES = (2**IPV6LENGTH) - 1 + _HEXTET_COUNT = 8 + _HEX_DIGITS = frozenset('0123456789ABCDEFabcdef') + + def __init__(self, address): + self._version = 6 + self._max_prefixlen = IPV6LENGTH + + def _ip_int_from_string(self, ip_str): + """Turn an IPv6 ip_str into an integer. + + Args: + ip_str: A string, the IPv6 ip_str. + + Returns: + A long, the IPv6 ip_str. + + Raises: + AddressValueError: if ip_str isn't a valid IPv6 Address. + + """ + parts = ip_str.split(':') + + # An IPv6 address needs at least 2 colons (3 parts). + if len(parts) < 3: + raise AddressValueError(ip_str) + + # If the address has an IPv4-style suffix, convert it to hexadecimal. + if '.' in parts[-1]: + ipv4_int = IPv4Address(parts.pop())._ip + parts.append('%x' % ((ipv4_int >> 16) & 0xFFFF)) + parts.append('%x' % (ipv4_int & 0xFFFF)) + + # An IPv6 address can't have more than 8 colons (9 parts). + if len(parts) > self._HEXTET_COUNT + 1: + raise AddressValueError(ip_str) + + # Disregarding the endpoints, find '::' with nothing in between. + # This indicates that a run of zeroes has been skipped. + try: + skip_index, = ( + [i for i in xrange(1, len(parts) - 1) if not parts[i]] or + [None]) + except ValueError: + # Can't have more than one '::' + raise AddressValueError(ip_str) + + # parts_hi is the number of parts to copy from above/before the '::' + # parts_lo is the number of parts to copy from below/after the '::' + if skip_index is not None: + # If we found a '::', then check if it also covers the endpoints. + parts_hi = skip_index + parts_lo = len(parts) - skip_index - 1 + if not parts[0]: + parts_hi -= 1 + if parts_hi: + raise AddressValueError(ip_str) # ^: requires ^:: + if not parts[-1]: + parts_lo -= 1 + if parts_lo: + raise AddressValueError(ip_str) # :$ requires ::$ + parts_skipped = self._HEXTET_COUNT - (parts_hi + parts_lo) + if parts_skipped < 1: + raise AddressValueError(ip_str) + else: + # Otherwise, allocate the entire address to parts_hi. The endpoints + # could still be empty, but _parse_hextet() will check for that. + if len(parts) != self._HEXTET_COUNT: + raise AddressValueError(ip_str) + parts_hi = len(parts) + parts_lo = 0 + parts_skipped = 0 + + try: + # Now, parse the hextets into a 128-bit integer. + ip_int = 0L + for i in xrange(parts_hi): + ip_int <<= 16 + ip_int |= self._parse_hextet(parts[i]) + ip_int <<= 16 * parts_skipped + for i in xrange(-parts_lo, 0): + ip_int <<= 16 + ip_int |= self._parse_hextet(parts[i]) + return ip_int + except ValueError: + raise AddressValueError(ip_str) + + def _parse_hextet(self, hextet_str): + """Convert an IPv6 hextet string into an integer. + + Args: + hextet_str: A string, the number to parse. + + Returns: + The hextet as an integer. + + Raises: + ValueError: if the input isn't strictly a hex number from [0..FFFF]. + + """ + # Whitelist the characters, since int() allows a lot of bizarre stuff. + if not self._HEX_DIGITS.issuperset(hextet_str): + raise ValueError + hextet_int = int(hextet_str, 16) + if hextet_int > 0xFFFF: + raise ValueError + return hextet_int + + def _compress_hextets(self, hextets): + """Compresses a list of hextets. + + Compresses a list of strings, replacing the longest continuous + sequence of "0" in the list with "" and adding empty strings at + the beginning or at the end of the string such that subsequently + calling ":".join(hextets) will produce the compressed version of + the IPv6 address. + + Args: + hextets: A list of strings, the hextets to compress. + + Returns: + A list of strings. + + """ + best_doublecolon_start = -1 + best_doublecolon_len = 0 + doublecolon_start = -1 + doublecolon_len = 0 + for index in range(len(hextets)): + if hextets[index] == '0': + doublecolon_len += 1 + if doublecolon_start == -1: + # Start of a sequence of zeros. + doublecolon_start = index + if doublecolon_len > best_doublecolon_len: + # This is the longest sequence of zeros so far. + best_doublecolon_len = doublecolon_len + best_doublecolon_start = doublecolon_start + else: + doublecolon_len = 0 + doublecolon_start = -1 + + if best_doublecolon_len > 1: + best_doublecolon_end = (best_doublecolon_start + + best_doublecolon_len) + # For zeros at the end of the address. + if best_doublecolon_end == len(hextets): + hextets += [''] + hextets[best_doublecolon_start:best_doublecolon_end] = [''] + # For zeros at the beginning of the address. + if best_doublecolon_start == 0: + hextets = [''] + hextets + + return hextets + + def _string_from_ip_int(self, ip_int=None): + """Turns a 128-bit integer into hexadecimal notation. + + Args: + ip_int: An integer, the IP address. + + Returns: + A string, the hexadecimal representation of the address. + + Raises: + ValueError: The address is bigger than 128 bits of all ones. + + """ + if not ip_int and ip_int != 0: + ip_int = int(self._ip) + + if ip_int > self._ALL_ONES: + raise ValueError('IPv6 address is too large') + + hex_str = '%032x' % ip_int + hextets = [] + for x in range(0, 32, 4): + hextets.append('%x' % int(hex_str[x:x+4], 16)) + + hextets = self._compress_hextets(hextets) + return ':'.join(hextets) + + def _explode_shorthand_ip_string(self, ip_str=None): + """Expand a shortened IPv6 address. + + Args: + ip_str: A string, the IPv6 address. + + Returns: + A string, the expanded IPv6 address. + + """ + if not ip_str: + ip_str = str(self) + if isinstance(self, _BaseNet): + ip_str = str(self.ip) + + ip_int = self._ip_int_from_string(ip_str) + parts = [] + for i in xrange(self._HEXTET_COUNT): + parts.append('%04x' % (ip_int & 0xFFFF)) + ip_int >>= 16 + parts.reverse() + return ':'.join(parts) + + @property + def max_prefixlen(self): + return self._max_prefixlen + + @property + def packed(self): + """The binary representation of this address.""" + return v6_int_to_packed(self._ip) + + @property + def version(self): + return self._version + + @property + def is_multicast(self): + """Test if the address is reserved for multicast use. + + Returns: + A boolean, True if the address is a multicast address. + See RFC 2373 2.7 for details. + + """ + return self in IPv6Network('ff00::/8') + + @property + def is_reserved(self): + """Test if the address is otherwise IETF reserved. + + Returns: + A boolean, True if the address is within one of the + reserved IPv6 Network ranges. + + """ + return (self in IPv6Network('::/8') or + self in IPv6Network('100::/8') or + self in IPv6Network('200::/7') or + self in IPv6Network('400::/6') or + self in IPv6Network('800::/5') or + self in IPv6Network('1000::/4') or + self in IPv6Network('4000::/3') or + self in IPv6Network('6000::/3') or + self in IPv6Network('8000::/3') or + self in IPv6Network('A000::/3') or + self in IPv6Network('C000::/3') or + self in IPv6Network('E000::/4') or + self in IPv6Network('F000::/5') or + self in IPv6Network('F800::/6') or + self in IPv6Network('FE00::/9')) + + @property + def is_unspecified(self): + """Test if the address is unspecified. + + Returns: + A boolean, True if this is the unspecified address as defined in + RFC 2373 2.5.2. + + """ + return self._ip == 0 and getattr(self, '_prefixlen', 128) == 128 + + @property + def is_loopback(self): + """Test if the address is a loopback address. + + Returns: + A boolean, True if the address is a loopback address as defined in + RFC 2373 2.5.3. + + """ + return self._ip == 1 and getattr(self, '_prefixlen', 128) == 128 + + @property + def is_link_local(self): + """Test if the address is reserved for link-local. + + Returns: + A boolean, True if the address is reserved per RFC 4291. + + """ + return self in IPv6Network('fe80::/10') + + @property + def is_site_local(self): + """Test if the address is reserved for site-local. + + Note that the site-local address space has been deprecated by RFC 3879. + Use is_private to test if this address is in the space of unique local + addresses as defined by RFC 4193. + + Returns: + A boolean, True if the address is reserved per RFC 3513 2.5.6. + + """ + return self in IPv6Network('fec0::/10') + + @property + def is_private(self): + """Test if this address is allocated for private networks. + + Returns: + A boolean, True if the address is reserved per RFC 4193. + + """ + return self in IPv6Network('fc00::/7') + + @property + def ipv4_mapped(self): + """Return the IPv4 mapped address. + + Returns: + If the IPv6 address is a v4 mapped address, return the + IPv4 mapped address. Return None otherwise. + + """ + if (self._ip >> 32) != 0xFFFF: + return None + return IPv4Address(self._ip & 0xFFFFFFFF) + + @property + def teredo(self): + """Tuple of embedded teredo IPs. + + Returns: + Tuple of the (server, client) IPs or None if the address + doesn't appear to be a teredo address (doesn't start with + 2001::/32) + + """ + if (self._ip >> 96) != 0x20010000: + return None + return (IPv4Address((self._ip >> 64) & 0xFFFFFFFF), + IPv4Address(~self._ip & 0xFFFFFFFF)) + + @property + def sixtofour(self): + """Return the IPv4 6to4 embedded address. + + Returns: + The IPv4 6to4-embedded address if present or None if the + address doesn't appear to contain a 6to4 embedded address. + + """ + if (self._ip >> 112) != 0x2002: + return None + return IPv4Address((self._ip >> 80) & 0xFFFFFFFF) + + +class IPv6Address(_BaseV6, _BaseIP): + + """Represent and manipulate single IPv6 Addresses. + """ + + def __init__(self, address): + """Instantiate a new IPv6 address object. + + Args: + address: A string or integer representing the IP + + Additionally, an integer can be passed, so + IPv6Address('2001:4860::') == + IPv6Address(42541956101370907050197289607612071936L). + or, more generally + IPv6Address(IPv6Address('2001:4860::')._ip) == + IPv6Address('2001:4860::') + + Raises: + AddressValueError: If address isn't a valid IPv6 address. + + """ + _BaseIP.__init__(self, address) + _BaseV6.__init__(self, address) + + # Efficient constructor from integer. + if isinstance(address, (int, long)): + self._ip = address + if address < 0 or address > self._ALL_ONES: + raise AddressValueError(address) + return + + # Constructing from a packed address + if _compat_has_real_bytes: + if isinstance(address, bytes) and len(address) == 16: + tmp = struct.unpack('!QQ', address) + self._ip = (tmp[0] << 64) | tmp[1] + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP string. + addr_str = str(address) + if not addr_str: + raise AddressValueError('') + + self._ip = self._ip_int_from_string(addr_str) + + +class IPv6Network(_BaseV6, _BaseNet): + + """This class represents and manipulates 128-bit IPv6 networks. + + Attributes: [examples for IPv6('2001:658:22A:CAFE:200::1/64')] + .ip: IPv6Address('2001:658:22a:cafe:200::1') + .network: IPv6Address('2001:658:22a:cafe::') + .hostmask: IPv6Address('::ffff:ffff:ffff:ffff') + .broadcast: IPv6Address('2001:658:22a:cafe:ffff:ffff:ffff:ffff') + .netmask: IPv6Address('ffff:ffff:ffff:ffff::') + .prefixlen: 64 + + """ + + + def __init__(self, address, strict=False): + """Instantiate a new IPv6 Network object. + + Args: + address: A string or integer representing the IPv6 network or the IP + and prefix/netmask. + '2001:4860::/128' + '2001:4860:0000:0000:0000:0000:0000:0000/128' + '2001:4860::' + are all functionally the same in IPv6. That is to say, + failing to provide a subnetmask will create an object with + a mask of /128. + + Additionally, an integer can be passed, so + IPv6Network('2001:4860::') == + IPv6Network(42541956101370907050197289607612071936L). + or, more generally + IPv6Network(IPv6Network('2001:4860::')._ip) == + IPv6Network('2001:4860::') + + strict: A boolean. If true, ensure that we have been passed + A true network address, eg, 192.168.1.0/24 and not an + IP address on a network, eg, 192.168.1.1/24. + + Raises: + AddressValueError: If address isn't a valid IPv6 address. + NetmaskValueError: If the netmask isn't valid for + an IPv6 address. + ValueError: If strict was True and a network address was not + supplied. + + """ + _BaseNet.__init__(self, address) + _BaseV6.__init__(self, address) + + # Efficient constructor from integer. + if isinstance(address, (int, long)): + self._ip = address + self.ip = IPv6Address(self._ip) + self._prefixlen = self._max_prefixlen + self.netmask = IPv6Address(self._ALL_ONES) + if address < 0 or address > self._ALL_ONES: + raise AddressValueError(address) + return + + # Constructing from a packed address + if _compat_has_real_bytes: + if isinstance(address, bytes) and len(address) == 16: + tmp = struct.unpack('!QQ', address) + self._ip = (tmp[0] << 64) | tmp[1] + self.ip = IPv6Address(self._ip) + self._prefixlen = self._max_prefixlen + self.netmask = IPv6Address(self._ALL_ONES) + return + + # Assume input argument to be string or any object representation + # which converts into a formatted IP prefix string. + addr = str(address).split('/') + + if len(addr) > 2: + raise AddressValueError(address) + + self._ip = self._ip_int_from_string(addr[0]) + self.ip = IPv6Address(self._ip) + + if len(addr) == 2: + if self._is_valid_netmask(addr[1]): + self._prefixlen = int(addr[1]) + else: + raise NetmaskValueError(addr[1]) + else: + self._prefixlen = self._max_prefixlen + + self.netmask = IPv6Address(self._ip_int_from_prefix(self._prefixlen)) + + if strict: + if self.ip != self.network: + raise ValueError('%s has host bits set' % + self.ip) + + def _is_valid_netmask(self, prefixlen): + """Verify that the netmask/prefixlen is valid. + + Args: + prefixlen: A string, the netmask in prefix length format. + + Returns: + A boolean, True if the prefix represents a valid IPv6 + netmask. + + """ + try: + prefixlen = int(prefixlen) + except ValueError: + return False + return 0 <= prefixlen <= self._max_prefixlen + + @property + def with_netmask(self): + return self.with_prefixlen -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed Nov 9 02:30:26 2011 From: jython-checkins at python.org (alan.kennedy) Date: Wed, 09 Nov 2011 02:30:26 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=282=2E5=29=3A_Multiple_updates?= =?utf8?q?_for_socket=2Egetaddrinfo_and_socket=2Egetnameinfo=2E?= Message-ID: http://hg.python.org/jython/rev/ca6b9103ea54 changeset: 6278:ca6b9103ea54 branch: 2.5 parent: 6276:49fb61a07b8d user: Alan Kennedy date: Wed Nov 09 01:18:07 2011 +0000 summary: Multiple updates for socket.getaddrinfo and socket.getnameinfo. Fixes http://bugs.jython.org/issue1706 files: Lib/socket.py | 142 +++++++++++++-- Lib/test/test_socket.py | 251 ++++++++++++++++++++++++++++ 2 files changed, 375 insertions(+), 18 deletions(-) diff --git a/Lib/socket.py b/Lib/socket.py --- a/Lib/socket.py +++ b/Lib/socket.py @@ -185,8 +185,20 @@ AI_ADDRCONFIG = 32 AI_NUMERICSERV = 1024 -EAI_NONAME = -2 -EAI_SERVICE = -8 +EAI_NONAME = -2 +EAI_SERVICE = -8 +EAI_ADDRFAMILY = -9 + +NI_NUMERICHOST = 1 +NI_NUMERICSERV = 2 +NI_NOFQDN = 4 +NI_NAMEREQD = 8 +NI_DGRAM = 16 +NI_MAXSERV = 32 +NI_IDN = 64 +NI_IDN_ALLOW_UNASSIGNED = 128 +NI_IDN_USE_STD3_ASCII_RULES = 256 +NI_MAXHOST = 1025 # 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 @@ -257,6 +269,24 @@ finally: sock_module = None +import _google_ipaddr_r234 + +def _is_ip_address(addr, version=None): + try: + _google_ipaddr_r234.IPAddress(addr, version) + return True + except ValueError: + return False + +def is_ipv4_address(addr): + return _is_ip_address(addr, 4) + +def is_ipv6_address(addr): + return _is_ip_address(addr, 6) + +def is_ip_address(addr): + return _is_ip_address(addr) + class _nio_impl: timeout = None @@ -625,24 +655,38 @@ # idna_libraries = [ - ('java.net.IDN', 'toASCII', java.lang.IllegalArgumentException) + ('java.net.IDN', 'toASCII', 'toUnicode', + 'ALLOW_UNASSIGNED', 'USE_STD3_ASCII_RULES', + java.lang.IllegalArgumentException) ] - -for idna_lib, idna_fn_name, exc in idna_libraries: + +for idna_lib, efn, dfn, au, usar, exc in idna_libraries: try: - m = __import__(idna_lib, globals(), locals(), [idna_fn_name]) - idna_fn = getattr(m, idna_fn_name) + m = __import__(idna_lib, globals(), locals(), [efn, dfn, au, usar]) + encode_fn = getattr(m, efn) def _encode_idna(name): try: - return idna_fn(name) + return encode_fn(name) except exc: raise UnicodeEncodeError(name) + decode_fn = getattr(m, dfn) + def _decode_idna(name, flags=0): + try: + jflags = 0 + if flags & NI_IDN_ALLOW_UNASSIGNED: + jflags |= au + if flags & NI_IDN_USE_STD3_ASCII_RULES: + jflags |= usar + return decode_fn(name, jflags) + except Exception, x: + raise UnicodeDecodeError(name) supports('idna', True) break except (AttributeError, ImportError), e: pass else: _encode_idna = lambda x: x.encode("ascii") + _decode_idna = lambda x, y=0: x.decode("ascii") # # Define data structures to support IPV4 and IPV6. @@ -734,18 +778,37 @@ pass return java.net.InetSocketAddress(java.net.InetAddress.getByName(hostname), port) +# Workaround for this (predominantly windows) issue +# http://wiki.python.org/jython/NewSocketModule#IPV6_address_support + _ipv4_addresses_only = False def _use_ipv4_addresses_only(value): global _ipv4_addresses_only _ipv4_addresses_only = value -def _get_port_number(port, flags): +def _getaddrinfo_get_host(host, family, flags): + if host is None: + return host + if not isinstance(host, basestring): + raise TypeError("getaddrinfo() argument 1 must be string or None") + if flags & AI_NUMERICHOST: + if not is_ip_address(host): + raise gaierror(EAI_NONAME, "Name or service not known") + if family == AF_INET and not is_ipv4_address(host): + raise gaierror(EAI_ADDRFAMILY, "Address family for hostname not supported") + if family == AF_INET6 and not is_ipv6_address(host): + raise gaierror(EAI_ADDRFAMILY, "Address family for hostname not supported") + if isinstance(host, unicode): + host = _encode_idna(host) + return host + +def _getaddrinfo_get_port(port, flags): if isinstance(port, basestring): try: int_port = int(port) except ValueError: - if flags and flags & AI_NUMERICSERV: + if flags & AI_NUMERICSERV: raise gaierror(EAI_NONAME, "Name or service not known") # Lookup the service by name try: @@ -760,23 +823,20 @@ int_port = int(port) return int_port % 65536 -def getaddrinfo(host, port, family=AF_INET, socktype=None, proto=0, flags=None): +def getaddrinfo(host, port, family=AF_INET, socktype=None, proto=0, flags=0): 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) + host = _getaddrinfo_get_host(host, family, flags) + port = _getaddrinfo_get_port(port, flags) filter_fns = [] 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): - 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 = [] @@ -798,8 +858,39 @@ except java.lang.Exception, jlx: raise _map_exception(jlx) +def _getnameinfo_get_host(address, flags): + if not isinstance(address, basestring): + raise TypeError("getnameinfo() address 1 must be string, not None") + if isinstance(address, unicode): + address = _encode_idna(address) + jia = java.net.InetAddress.getByName(address) + result = jia.getCanonicalHostName() + if flags & NI_NAMEREQD: + if is_ip_address(result): + raise gaierror(EAI_NONAME, "Name or service not known") + elif flags & NI_NUMERICHOST: + result = jia.getHostAddress() + # Ignoring NI_NOFQDN for now + if flags & NI_IDN: + result = _decode_idna(result, flags) + return result + +def _getnameinfo_get_port(port, flags): + if not isinstance(port, (int, long)): + raise TypeError("getnameinfo() port number must be an integer") + if flags & NI_NUMERICSERV: + return port + proto = None + if flags & NI_DGRAM: + proto = "udp" + return getservbyport(port, proto) + def getnameinfo(sock_addr, flags): - raise NotImplementedError("getnameinfo not yet supported on jython.") + if not isinstance(sock_addr, tuple) or len(sock_addr) < 2: + raise TypeError("getnameinfo() argument 1 must be a tuple") + host = _getnameinfo_get_host(sock_addr[0], flags) + port = _getnameinfo_get_port(sock_addr[1], flags) + return (host, port) def getdefaulttimeout(): return _defaulttimeout @@ -830,8 +921,15 @@ def ntohl(x): return x def inet_pton(family, ip_string): - # FIXME: java.net.InetAddress.getByName also accepts hostnames try: + if family == AF_INET: + if not is_ipv4_address(ip_string): + raise error("illegal IP address string passed to inet_pton") + elif family == AF_INET6: + if not is_ipv6_address(ip_string): + raise error("illegal IP address string passed to inet_pton") + else: + raise error(errno.EAFNOSUPPORT, "Address family not supported by protocol") ia = java.net.InetAddress.getByName(ip_string) bytes = [] for byte in ia.getAddress(): @@ -846,6 +944,14 @@ def inet_ntop(family, packed_ip): try: jByteArray = jarray.array(packed_ip, 'b') + if family == AF_INET: + if len(jByteArray) != 4: + raise ValueError("invalid length of packed IP address string") + elif family == AF_INET6: + if len(jByteArray) != 16: + raise ValueError("invalid length of packed IP address string") + else: + raise ValueError("unknown address family %s" % family) ia = java.net.InetAddress.getByAddress(jByteArray) return ia.getHostAddress() except java.lang.Exception, jlx: 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 @@ -8,6 +8,7 @@ from test import test_support import errno +import jarray import Queue import platform import select @@ -474,6 +475,31 @@ f('45ef:76cb:1a:56ef:afeb:bac:1924:aeae') ) + def test_inet_pton_exceptions(self): + if not hasattr(socket, 'inet_pton'): + return # No inet_pton() on this platform + + try: + socket.inet_pton(socket.AF_UNSPEC, "doesntmatter") + except socket.error, se: + self.failUnlessEqual(se[0], errno.EAFNOSUPPORT) + except Exception, x: + self.fail("inet_pton raised wrong exception for incorrect address family AF_UNSPEC: %s" % str(x)) + + try: + socket.inet_pton(socket.AF_INET, "1.2.3.") + except socket.error, se: + pass + except Exception, x: + self.fail("inet_pton raised wrong exception for invalid AF_INET address: %s" % str(x)) + + try: + socket.inet_pton(socket.AF_INET6, ":::") + except socket.error, se: + pass + except Exception, x: + self.fail("inet_pton raised wrong exception for invalid AF_INET6 address: %s" % str(x)) + def testStringToIPv4(self): if not hasattr(socket, 'inet_ntop'): return # No inet_ntop() on this platform @@ -510,6 +536,33 @@ f('\x0a\xef\x0b\x01\x05\x06\x10\x01\xff\xff\x99\x97\x00\x55\x01\x70') ) + def test_inet_ntop_exceptions(self): + if not hasattr(socket, 'inet_ntop'): + return # No inet_ntop() on this platform + valid_address = '\x01\x01\x01\x01' + invalid_address = '\x01\x01\x01\x01\x01' + + try: + socket.inet_ntop(socket.AF_UNSPEC, valid_address) + except ValueError, v: + pass + except Exception, x: + self.fail("inet_ntop raised wrong exception for incorrect address family AF_UNSPEC: %s" % str(x)) + + try: + socket.inet_ntop(socket.AF_INET, invalid_address) + except ValueError, v: + pass + except Exception, x: + self.fail("inet_ntop raised wrong exception for invalid AF_INET address: %s" % str(x)) + + try: + socket.inet_ntop(socket.AF_INET6, invalid_address) + except ValueError, v: + pass + except Exception, x: + self.fail("inet_ntop raised wrong exception for invalid AF_INET address: %s" % str(x)) + # XXX The following don't test module-level functionality... def testSockName(self): @@ -552,6 +605,94 @@ sock.close() self.assertRaises(socket.error, sock.send, "spam") +class IPAddressTests(unittest.TestCase): + + def testValidIpV4Addresses(self): + for a in [ + "0.0.0.1", + "1.0.0.1", + "127.0.0.1", + "255.12.34.56", + "255.255.255.255", + ]: + self.failUnless(socket.is_ipv4_address(a), "is_ipv4_address failed for valid IPV4 address '%s'" % a) + self.failUnless(socket.is_ip_address(a), "is_ip_address failed for valid IPV4 address '%s'" % a) + + def testInvalidIpV4Addresses(self): + for a in [ + "99.2", + "99.2.4", + "-10.1.2.3", + "256.0.0.0", + "0.256.0.0", + "0.0.256.0", + "0.0.0.256", + "255.24.x.100", + "255.24.-1.128", + "255.24.-1.128.", + "255.0.0.999", + ]: + self.failUnless(not socket.is_ipv4_address(a), "not is_ipv4_address failed for invalid IPV4 address '%s'" % a) + self.failUnless(not socket.is_ip_address(a), "not is_ip_address failed for invalid IPV4 address '%s'" % a) + + def testValidIpV6Addresses(self): + for a in [ + "::", + "::1", + "fe80::1", + "::192.168.1.1", + "0:0:0:0:0:0:0:0", + "1080::8:800:2C:4A", + "FEC0:0:0:0:0:0:0:1", + "::FFFF:192.168.1.1", + "abcd:ef:111:f123::1", + "1138:0:0:0:8:80:800:417A", + "fecc:face::b00c:f001:fedc:fedd", + "CaFe:BaBe:dEAd:BeeF:12:345:6789:abcd", + ]: + self.failUnless(socket.is_ipv6_address(a), "is_ipv6_address failed for valid IPV6 address '%s'" % a) + self.failUnless(socket.is_ip_address(a), "is_ip_address failed for valid IPV6 address '%s'" % a) + + def testInvalidIpV6Addresses(self): + for a in [ + "2001:db8:::192.0.2.1", # from RFC 5954 + "CaFe:BaBe:dEAd:BeeF:12:345:6789:abcd:", + "CaFe:BaBe:dEAd:BeeF:12:345:6789:abcd:ef", + "CaFFe:1a77e:dEAd:BeeF:12:345:6789:abcd", + ]: + self.failUnless(not socket.is_ipv6_address(a), "not is_ipv6_address failed for invalid IPV6 address '%s'" % a) + self.failUnless(not socket.is_ip_address(a), "not is_ip_address failed for invalid IPV6 address '%s'" % a) + + def testRFC5952(self): + for a in [ + "2001:db8::", + "2001:db8::1", + "2001:db8:0::1", + "2001:db8:0:0::1", + "2001:db8:0:0:0::1", + "2001:DB8:0:0:1::1", + "2001:db8:0:0:1::1", + "2001:db8::1:0:0:1", + "2001:0db8::1:0:0:1", + "2001:db8::0:1:0:0:1", + "2001:db8:0:0:1:0:0:1", + "2001:db8:0000:0:1::1", + "2001:db8::aaaa:0:0:1", + "2001:db8:0:0:aaaa::1", + "2001:0db8:0:0:1:0:0:1", + "2001:db8:aaaa:bbbb:cccc:dddd::1", + "2001:db8:aaaa:bbbb:cccc:dddd:0:1", + "2001:db8:aaaa:bbbb:cccc:dddd:eeee:1", + "2001:db8:aaaa:bbbb:cccc:dddd:eeee:01", + "2001:db8:aaaa:bbbb:cccc:dddd:eeee:001", + "2001:db8:aaaa:bbbb:cccc:dddd:eeee:0001", + "2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa", + "2001:db8:aaaa:bbbb:cccc:dddd:eeee:AAAA", + "2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa", + ]: + self.failUnless(socket.is_ipv6_address(a), "is_ipv6_address failed for valid RFC 5952 IPV6 address '%s'" % a) + self.failUnless(socket.is_ip_address(a), "is_ip_address failed for valid RFC 5952 IPV6 address '%s'" % a) + class TestSocketOptions(unittest.TestCase): def setUp(self): @@ -1630,6 +1771,114 @@ else: self.fail("getaddrinfo for unknown service name failed to raise exception") + def testHostNames(self): + # None is always acceptable + for flags in [0, socket.AI_NUMERICHOST]: + try: + socket.getaddrinfo(None, 80, 0, 0, 0, flags) + except Exception, x: + self.fail("hostname == None should not have raised exception: %s" % str(x)) + + # Check enforcement of AI_NUMERICHOST + for host in ["", " ", "localhost"]: + try: + socket.getaddrinfo(host, 80, 0, 0, 0, socket.AI_NUMERICHOST) + except socket.gaierror, ge: + self.failUnlessEqual(ge[0], socket.EAI_NONAME) + except Exception, x: + self.fail("Non-numeric host with AI_NUMERICHOST raised wrong exception: %s" % str(x)) + else: + self.fail("Non-numeric hostname '%s' with AI_NUMERICHOST should have raised exception" % host) + + # Check enforcement of AI_NUMERICHOST with wrong address families + for host, family in [("127.0.0.1", socket.AF_INET6), ("::1", socket.AF_INET)]: + try: + socket.getaddrinfo(host, 80, family, 0, 0, socket.AI_NUMERICHOST) + except socket.gaierror, ge: + self.failUnlessEqual(ge[0], socket.EAI_ADDRFAMILY) + except Exception, x: + self.fail("Numeric host '%s' in wrong family '%s' with AI_NUMERICHOST raised wrong exception: %s" % + (host, family, str(x)) ) + else: + self.fail("Numeric host '%s' in wrong family '%s' with AI_NUMERICHOST should have raised exception" % + (host, family) ) + +class TestGetNameInfo(unittest.TestCase): + + def testBadParameters(self): + for address, flags in [ + ( (0,0), 0), + ( (0,"http"), 0), + ( "localhost", 0), + ( 0, 0), + ( ("",), 0), + ]: + try: + socket.getnameinfo(address, flags) + except TypeError: + pass + except Exception, x: + self.fail("Bad getnameinfo parameters (%s, %s) raised wrong exception: %s" % (str(address), flags, str(x))) + else: + self.fail("Bad getnameinfo parameters (%s, %s) failed to raise exception" % (str(address), flags)) + + def testPort(self): + for address, flags, expected in [ + ( ("127.0.0.1", 25), 0, "smtp" ), + ( ("127.0.0.1", 25), socket.NI_NUMERICSERV, 25 ), + ( ("127.0.0.1", 513), socket.NI_DGRAM, "who" ), + ( ("127.0.0.1", 513), 0, "login"), + ]: + result = socket.getnameinfo(address, flags) + self.failUnlessEqual(result[1], expected) + + def testHost(self): + for address, flags, expected in [ + ( ("www.python.org", 80), 0, "dinsdale.python.org"), + ( ("www.python.org", 80), socket.NI_NUMERICHOST, "82.94.164.162" ), + ( ("www.python.org", 80), socket.NI_NAMEREQD, "dinsdale.python.org"), + ( ("82.94.164.162", 80), socket.NI_NAMEREQD, "dinsdale.python.org"), + ]: + result = socket.getnameinfo(address, flags) + self.failUnlessEqual(result[0], expected) + + def testNI_NAMEREQD(self): + # This test may delay for some seconds + unreversible_address = "198.51.100.1" + try: + socket.getnameinfo( (unreversible_address, 80), socket.NI_NAMEREQD) + except socket.gaierror, ge: + self.failUnlessEqual(ge[0], socket.EAI_NONAME) + except Exception, x: + self.fail("Unreversible address with NI_NAMEREQD (%s) raised wrong exception: %s" % (unreversible_address, str(x))) + else: + self.fail("Unreversible address with NI_NAMEREQD (%s) failed to raise exception" % unreversible_address) + + def testHostIdna(self): + fqdn = u"\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u043e.\u0440\u0444" + idn = "xn--80aealotwbjpid2k.xn--p1ai" + ip = "95.173.135.62" + try: + import java.net.IDN + except ImportError: + try: + socket.getnameinfo( (fqdn, 80), 0) + except UnicodeEncodeError: + pass + except Exception, x: + self.fail("International domain without java.net.IDN raised wrong exception: %s" % str(x)) + else: + self.fail("International domain without java.net.IDN failed to raise exception") + else: + # have to disable this test until I find an IDN that reverses to the punycode name + return + for address, flags, expected in [ + ( (fqdn, 80), 0, idn ), + ( (fqdn, 80), socket.NI_IDN, fqdn ), + ]: + result = socket.getnameinfo(address, flags) + self.failUnlessEqual(result[0], expected) + class TestJython_get_jsockaddr(unittest.TestCase): "These tests are specific to jython: they test a key internal routine" @@ -1919,6 +2168,7 @@ def test_main(): tests = [ GeneralModuleTests, + IPAddressTests, TestSupportedOptions, TestUnsupportedOptions, BasicTCPTest, @@ -1927,6 +2177,7 @@ TestExceptions, TestInvalidUsage, TestGetAddrInfo, + TestGetNameInfo, TestTCPAddressParameters, TestUDPAddressParameters, UDPBindTest, -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed Nov 9 02:30:27 2011 From: jython-checkins at python.org (alan.kennedy) Date: Wed, 09 Nov 2011 02:30:27 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf8?q?=3A_merge_w/2=2E5=3A_Multiple_updates_for_socket=2Egetaddrinfo_a?= =?utf8?q?nd_socket=2Egetnameinfo=2E?= Message-ID: http://hg.python.org/jython/rev/c280db0483aa changeset: 6279:c280db0483aa parent: 6277:2f59a06f3e0f parent: 6278:ca6b9103ea54 user: Alan Kennedy date: Wed Nov 09 01:23:08 2011 +0000 summary: merge w/2.5: Multiple updates for socket.getaddrinfo and socket.getnameinfo. Fixes http://bugs.jython.org/issue1706 files: Lib/socket.py | 142 +++++++++++++-- Lib/test/test_socket.py | 251 ++++++++++++++++++++++++++++ 2 files changed, 375 insertions(+), 18 deletions(-) diff --git a/Lib/socket.py b/Lib/socket.py --- a/Lib/socket.py +++ b/Lib/socket.py @@ -185,8 +185,20 @@ AI_ADDRCONFIG = 32 AI_NUMERICSERV = 1024 -EAI_NONAME = -2 -EAI_SERVICE = -8 +EAI_NONAME = -2 +EAI_SERVICE = -8 +EAI_ADDRFAMILY = -9 + +NI_NUMERICHOST = 1 +NI_NUMERICSERV = 2 +NI_NOFQDN = 4 +NI_NAMEREQD = 8 +NI_DGRAM = 16 +NI_MAXSERV = 32 +NI_IDN = 64 +NI_IDN_ALLOW_UNASSIGNED = 128 +NI_IDN_USE_STD3_ASCII_RULES = 256 +NI_MAXHOST = 1025 # 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 @@ -257,6 +269,24 @@ finally: sock_module = None +import _google_ipaddr_r234 + +def _is_ip_address(addr, version=None): + try: + _google_ipaddr_r234.IPAddress(addr, version) + return True + except ValueError: + return False + +def is_ipv4_address(addr): + return _is_ip_address(addr, 4) + +def is_ipv6_address(addr): + return _is_ip_address(addr, 6) + +def is_ip_address(addr): + return _is_ip_address(addr) + class _nio_impl: timeout = None @@ -625,24 +655,38 @@ # idna_libraries = [ - ('java.net.IDN', 'toASCII', java.lang.IllegalArgumentException) + ('java.net.IDN', 'toASCII', 'toUnicode', + 'ALLOW_UNASSIGNED', 'USE_STD3_ASCII_RULES', + java.lang.IllegalArgumentException) ] - -for idna_lib, idna_fn_name, exc in idna_libraries: + +for idna_lib, efn, dfn, au, usar, exc in idna_libraries: try: - m = __import__(idna_lib, globals(), locals(), [idna_fn_name]) - idna_fn = getattr(m, idna_fn_name) + m = __import__(idna_lib, globals(), locals(), [efn, dfn, au, usar]) + encode_fn = getattr(m, efn) def _encode_idna(name): try: - return idna_fn(name) + return encode_fn(name) except exc: raise UnicodeEncodeError(name) + decode_fn = getattr(m, dfn) + def _decode_idna(name, flags=0): + try: + jflags = 0 + if flags & NI_IDN_ALLOW_UNASSIGNED: + jflags |= au + if flags & NI_IDN_USE_STD3_ASCII_RULES: + jflags |= usar + return decode_fn(name, jflags) + except Exception, x: + raise UnicodeDecodeError(name) supports('idna', True) break except (AttributeError, ImportError), e: pass else: _encode_idna = lambda x: x.encode("ascii") + _decode_idna = lambda x, y=0: x.decode("ascii") # # Define data structures to support IPV4 and IPV6. @@ -734,18 +778,37 @@ pass return java.net.InetSocketAddress(java.net.InetAddress.getByName(hostname), port) +# Workaround for this (predominantly windows) issue +# http://wiki.python.org/jython/NewSocketModule#IPV6_address_support + _ipv4_addresses_only = False def _use_ipv4_addresses_only(value): global _ipv4_addresses_only _ipv4_addresses_only = value -def _get_port_number(port, flags): +def _getaddrinfo_get_host(host, family, flags): + if host is None: + return host + if not isinstance(host, basestring): + raise TypeError("getaddrinfo() argument 1 must be string or None") + if flags & AI_NUMERICHOST: + if not is_ip_address(host): + raise gaierror(EAI_NONAME, "Name or service not known") + if family == AF_INET and not is_ipv4_address(host): + raise gaierror(EAI_ADDRFAMILY, "Address family for hostname not supported") + if family == AF_INET6 and not is_ipv6_address(host): + raise gaierror(EAI_ADDRFAMILY, "Address family for hostname not supported") + if isinstance(host, unicode): + host = _encode_idna(host) + return host + +def _getaddrinfo_get_port(port, flags): if isinstance(port, basestring): try: int_port = int(port) except ValueError: - if flags and flags & AI_NUMERICSERV: + if flags & AI_NUMERICSERV: raise gaierror(EAI_NONAME, "Name or service not known") # Lookup the service by name try: @@ -760,23 +823,20 @@ int_port = int(port) return int_port % 65536 -def getaddrinfo(host, port, family=AF_INET, socktype=None, proto=0, flags=None): +def getaddrinfo(host, port, family=AF_INET, socktype=None, proto=0, flags=0): 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) + host = _getaddrinfo_get_host(host, family, flags) + port = _getaddrinfo_get_port(port, flags) filter_fns = [] 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): - 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 = [] @@ -798,8 +858,39 @@ except java.lang.Exception, jlx: raise _map_exception(jlx) +def _getnameinfo_get_host(address, flags): + if not isinstance(address, basestring): + raise TypeError("getnameinfo() address 1 must be string, not None") + if isinstance(address, unicode): + address = _encode_idna(address) + jia = java.net.InetAddress.getByName(address) + result = jia.getCanonicalHostName() + if flags & NI_NAMEREQD: + if is_ip_address(result): + raise gaierror(EAI_NONAME, "Name or service not known") + elif flags & NI_NUMERICHOST: + result = jia.getHostAddress() + # Ignoring NI_NOFQDN for now + if flags & NI_IDN: + result = _decode_idna(result, flags) + return result + +def _getnameinfo_get_port(port, flags): + if not isinstance(port, (int, long)): + raise TypeError("getnameinfo() port number must be an integer") + if flags & NI_NUMERICSERV: + return port + proto = None + if flags & NI_DGRAM: + proto = "udp" + return getservbyport(port, proto) + def getnameinfo(sock_addr, flags): - raise NotImplementedError("getnameinfo not yet supported on jython.") + if not isinstance(sock_addr, tuple) or len(sock_addr) < 2: + raise TypeError("getnameinfo() argument 1 must be a tuple") + host = _getnameinfo_get_host(sock_addr[0], flags) + port = _getnameinfo_get_port(sock_addr[1], flags) + return (host, port) def getdefaulttimeout(): return _defaulttimeout @@ -830,8 +921,15 @@ def ntohl(x): return x def inet_pton(family, ip_string): - # FIXME: java.net.InetAddress.getByName also accepts hostnames try: + if family == AF_INET: + if not is_ipv4_address(ip_string): + raise error("illegal IP address string passed to inet_pton") + elif family == AF_INET6: + if not is_ipv6_address(ip_string): + raise error("illegal IP address string passed to inet_pton") + else: + raise error(errno.EAFNOSUPPORT, "Address family not supported by protocol") ia = java.net.InetAddress.getByName(ip_string) bytes = [] for byte in ia.getAddress(): @@ -846,6 +944,14 @@ def inet_ntop(family, packed_ip): try: jByteArray = jarray.array(packed_ip, 'b') + if family == AF_INET: + if len(jByteArray) != 4: + raise ValueError("invalid length of packed IP address string") + elif family == AF_INET6: + if len(jByteArray) != 16: + raise ValueError("invalid length of packed IP address string") + else: + raise ValueError("unknown address family %s" % family) ia = java.net.InetAddress.getByAddress(jByteArray) return ia.getHostAddress() except java.lang.Exception, jlx: 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 @@ -8,6 +8,7 @@ from test import test_support import errno +import jarray import Queue import platform import select @@ -474,6 +475,31 @@ f('45ef:76cb:1a:56ef:afeb:bac:1924:aeae') ) + def test_inet_pton_exceptions(self): + if not hasattr(socket, 'inet_pton'): + return # No inet_pton() on this platform + + try: + socket.inet_pton(socket.AF_UNSPEC, "doesntmatter") + except socket.error, se: + self.failUnlessEqual(se[0], errno.EAFNOSUPPORT) + except Exception, x: + self.fail("inet_pton raised wrong exception for incorrect address family AF_UNSPEC: %s" % str(x)) + + try: + socket.inet_pton(socket.AF_INET, "1.2.3.") + except socket.error, se: + pass + except Exception, x: + self.fail("inet_pton raised wrong exception for invalid AF_INET address: %s" % str(x)) + + try: + socket.inet_pton(socket.AF_INET6, ":::") + except socket.error, se: + pass + except Exception, x: + self.fail("inet_pton raised wrong exception for invalid AF_INET6 address: %s" % str(x)) + def testStringToIPv4(self): if not hasattr(socket, 'inet_ntop'): return # No inet_ntop() on this platform @@ -510,6 +536,33 @@ f('\x0a\xef\x0b\x01\x05\x06\x10\x01\xff\xff\x99\x97\x00\x55\x01\x70') ) + def test_inet_ntop_exceptions(self): + if not hasattr(socket, 'inet_ntop'): + return # No inet_ntop() on this platform + valid_address = '\x01\x01\x01\x01' + invalid_address = '\x01\x01\x01\x01\x01' + + try: + socket.inet_ntop(socket.AF_UNSPEC, valid_address) + except ValueError, v: + pass + except Exception, x: + self.fail("inet_ntop raised wrong exception for incorrect address family AF_UNSPEC: %s" % str(x)) + + try: + socket.inet_ntop(socket.AF_INET, invalid_address) + except ValueError, v: + pass + except Exception, x: + self.fail("inet_ntop raised wrong exception for invalid AF_INET address: %s" % str(x)) + + try: + socket.inet_ntop(socket.AF_INET6, invalid_address) + except ValueError, v: + pass + except Exception, x: + self.fail("inet_ntop raised wrong exception for invalid AF_INET address: %s" % str(x)) + # XXX The following don't test module-level functionality... def testSockName(self): @@ -552,6 +605,94 @@ sock.close() self.assertRaises(socket.error, sock.send, "spam") +class IPAddressTests(unittest.TestCase): + + def testValidIpV4Addresses(self): + for a in [ + "0.0.0.1", + "1.0.0.1", + "127.0.0.1", + "255.12.34.56", + "255.255.255.255", + ]: + self.failUnless(socket.is_ipv4_address(a), "is_ipv4_address failed for valid IPV4 address '%s'" % a) + self.failUnless(socket.is_ip_address(a), "is_ip_address failed for valid IPV4 address '%s'" % a) + + def testInvalidIpV4Addresses(self): + for a in [ + "99.2", + "99.2.4", + "-10.1.2.3", + "256.0.0.0", + "0.256.0.0", + "0.0.256.0", + "0.0.0.256", + "255.24.x.100", + "255.24.-1.128", + "255.24.-1.128.", + "255.0.0.999", + ]: + self.failUnless(not socket.is_ipv4_address(a), "not is_ipv4_address failed for invalid IPV4 address '%s'" % a) + self.failUnless(not socket.is_ip_address(a), "not is_ip_address failed for invalid IPV4 address '%s'" % a) + + def testValidIpV6Addresses(self): + for a in [ + "::", + "::1", + "fe80::1", + "::192.168.1.1", + "0:0:0:0:0:0:0:0", + "1080::8:800:2C:4A", + "FEC0:0:0:0:0:0:0:1", + "::FFFF:192.168.1.1", + "abcd:ef:111:f123::1", + "1138:0:0:0:8:80:800:417A", + "fecc:face::b00c:f001:fedc:fedd", + "CaFe:BaBe:dEAd:BeeF:12:345:6789:abcd", + ]: + self.failUnless(socket.is_ipv6_address(a), "is_ipv6_address failed for valid IPV6 address '%s'" % a) + self.failUnless(socket.is_ip_address(a), "is_ip_address failed for valid IPV6 address '%s'" % a) + + def testInvalidIpV6Addresses(self): + for a in [ + "2001:db8:::192.0.2.1", # from RFC 5954 + "CaFe:BaBe:dEAd:BeeF:12:345:6789:abcd:", + "CaFe:BaBe:dEAd:BeeF:12:345:6789:abcd:ef", + "CaFFe:1a77e:dEAd:BeeF:12:345:6789:abcd", + ]: + self.failUnless(not socket.is_ipv6_address(a), "not is_ipv6_address failed for invalid IPV6 address '%s'" % a) + self.failUnless(not socket.is_ip_address(a), "not is_ip_address failed for invalid IPV6 address '%s'" % a) + + def testRFC5952(self): + for a in [ + "2001:db8::", + "2001:db8::1", + "2001:db8:0::1", + "2001:db8:0:0::1", + "2001:db8:0:0:0::1", + "2001:DB8:0:0:1::1", + "2001:db8:0:0:1::1", + "2001:db8::1:0:0:1", + "2001:0db8::1:0:0:1", + "2001:db8::0:1:0:0:1", + "2001:db8:0:0:1:0:0:1", + "2001:db8:0000:0:1::1", + "2001:db8::aaaa:0:0:1", + "2001:db8:0:0:aaaa::1", + "2001:0db8:0:0:1:0:0:1", + "2001:db8:aaaa:bbbb:cccc:dddd::1", + "2001:db8:aaaa:bbbb:cccc:dddd:0:1", + "2001:db8:aaaa:bbbb:cccc:dddd:eeee:1", + "2001:db8:aaaa:bbbb:cccc:dddd:eeee:01", + "2001:db8:aaaa:bbbb:cccc:dddd:eeee:001", + "2001:db8:aaaa:bbbb:cccc:dddd:eeee:0001", + "2001:db8:aaaa:bbbb:cccc:dddd:eeee:aaaa", + "2001:db8:aaaa:bbbb:cccc:dddd:eeee:AAAA", + "2001:db8:aaaa:bbbb:cccc:dddd:eeee:AaAa", + ]: + self.failUnless(socket.is_ipv6_address(a), "is_ipv6_address failed for valid RFC 5952 IPV6 address '%s'" % a) + self.failUnless(socket.is_ip_address(a), "is_ip_address failed for valid RFC 5952 IPV6 address '%s'" % a) + class TestSocketOptions(unittest.TestCase): def setUp(self): @@ -1630,6 +1771,114 @@ else: self.fail("getaddrinfo for unknown service name failed to raise exception") + def testHostNames(self): + # None is always acceptable + for flags in [0, socket.AI_NUMERICHOST]: + try: + socket.getaddrinfo(None, 80, 0, 0, 0, flags) + except Exception, x: + self.fail("hostname == None should not have raised exception: %s" % str(x)) + + # Check enforcement of AI_NUMERICHOST + for host in ["", " ", "localhost"]: + try: + socket.getaddrinfo(host, 80, 0, 0, 0, socket.AI_NUMERICHOST) + except socket.gaierror, ge: + self.failUnlessEqual(ge[0], socket.EAI_NONAME) + except Exception, x: + self.fail("Non-numeric host with AI_NUMERICHOST raised wrong exception: %s" % str(x)) + else: + self.fail("Non-numeric hostname '%s' with AI_NUMERICHOST should have raised exception" % host) + + # Check enforcement of AI_NUMERICHOST with wrong address families + for host, family in [("127.0.0.1", socket.AF_INET6), ("::1", socket.AF_INET)]: + try: + socket.getaddrinfo(host, 80, family, 0, 0, socket.AI_NUMERICHOST) + except socket.gaierror, ge: + self.failUnlessEqual(ge[0], socket.EAI_ADDRFAMILY) + except Exception, x: + self.fail("Numeric host '%s' in wrong family '%s' with AI_NUMERICHOST raised wrong exception: %s" % + (host, family, str(x)) ) + else: + self.fail("Numeric host '%s' in wrong family '%s' with AI_NUMERICHOST should have raised exception" % + (host, family) ) + +class TestGetNameInfo(unittest.TestCase): + + def testBadParameters(self): + for address, flags in [ + ( (0,0), 0), + ( (0,"http"), 0), + ( "localhost", 0), + ( 0, 0), + ( ("",), 0), + ]: + try: + socket.getnameinfo(address, flags) + except TypeError: + pass + except Exception, x: + self.fail("Bad getnameinfo parameters (%s, %s) raised wrong exception: %s" % (str(address), flags, str(x))) + else: + self.fail("Bad getnameinfo parameters (%s, %s) failed to raise exception" % (str(address), flags)) + + def testPort(self): + for address, flags, expected in [ + ( ("127.0.0.1", 25), 0, "smtp" ), + ( ("127.0.0.1", 25), socket.NI_NUMERICSERV, 25 ), + ( ("127.0.0.1", 513), socket.NI_DGRAM, "who" ), + ( ("127.0.0.1", 513), 0, "login"), + ]: + result = socket.getnameinfo(address, flags) + self.failUnlessEqual(result[1], expected) + + def testHost(self): + for address, flags, expected in [ + ( ("www.python.org", 80), 0, "dinsdale.python.org"), + ( ("www.python.org", 80), socket.NI_NUMERICHOST, "82.94.164.162" ), + ( ("www.python.org", 80), socket.NI_NAMEREQD, "dinsdale.python.org"), + ( ("82.94.164.162", 80), socket.NI_NAMEREQD, "dinsdale.python.org"), + ]: + result = socket.getnameinfo(address, flags) + self.failUnlessEqual(result[0], expected) + + def testNI_NAMEREQD(self): + # This test may delay for some seconds + unreversible_address = "198.51.100.1" + try: + socket.getnameinfo( (unreversible_address, 80), socket.NI_NAMEREQD) + except socket.gaierror, ge: + self.failUnlessEqual(ge[0], socket.EAI_NONAME) + except Exception, x: + self.fail("Unreversible address with NI_NAMEREQD (%s) raised wrong exception: %s" % (unreversible_address, str(x))) + else: + self.fail("Unreversible address with NI_NAMEREQD (%s) failed to raise exception" % unreversible_address) + + def testHostIdna(self): + fqdn = u"\u043f\u0440\u0430\u0432\u0438\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u043e.\u0440\u0444" + idn = "xn--80aealotwbjpid2k.xn--p1ai" + ip = "95.173.135.62" + try: + import java.net.IDN + except ImportError: + try: + socket.getnameinfo( (fqdn, 80), 0) + except UnicodeEncodeError: + pass + except Exception, x: + self.fail("International domain without java.net.IDN raised wrong exception: %s" % str(x)) + else: + self.fail("International domain without java.net.IDN failed to raise exception") + else: + # have to disable this test until I find an IDN that reverses to the punycode name + return + for address, flags, expected in [ + ( (fqdn, 80), 0, idn ), + ( (fqdn, 80), socket.NI_IDN, fqdn ), + ]: + result = socket.getnameinfo(address, flags) + self.failUnlessEqual(result[0], expected) + class TestJython_get_jsockaddr(unittest.TestCase): "These tests are specific to jython: they test a key internal routine" @@ -1919,6 +2168,7 @@ def test_main(): tests = [ GeneralModuleTests, + IPAddressTests, TestSupportedOptions, TestUnsupportedOptions, BasicTCPTest, @@ -1927,6 +2177,7 @@ TestExceptions, TestInvalidUsage, TestGetAddrInfo, + TestGetNameInfo, TestTCPAddressParameters, TestUDPAddressParameters, UDPBindTest, -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Wed Nov 23 07:29:28 2011 From: jython-checkins at python.org (oti.humbel) Date: Wed, 23 Nov 2011 07:29:28 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=282=2E5=29=3A_enable_tests_to_?= =?utf8?q?be_run_from_Jenkins_on_Fedora_by_changing_port_numbers?= Message-ID: http://hg.python.org/jython/rev/8af641b6973e changeset: 6280:8af641b6973e branch: 2.5 parent: 6278:ca6b9103ea54 user: Oti Humbel date: Wed Nov 23 07:28:40 2011 +0100 summary: enable tests to be run from Jenkins on Fedora by changing port numbers files: Lib/test/test_socket.py | 2 +- Lib/test/test_urllib2_localnet.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 @@ -20,7 +20,7 @@ from weakref import proxy from StringIO import StringIO -PORT = 50007 +PORT = 50100 HOST = 'localhost' MSG = 'Michael Gilfix was here\n' EIGHT_BIT_MSG = 'Bh\xed Al\xe1in \xd3 Cinn\xe9ide anseo\n' diff --git a/Lib/test/test_urllib2_localnet.py b/Lib/test/test_urllib2_localnet.py --- a/Lib/test/test_urllib2_localnet.py +++ b/Lib/test/test_urllib2_localnet.py @@ -226,7 +226,7 @@ class ProxyAuthTests(unittest.TestCase): URL = "http://www.foo.com" - PORT = 8080 + PORT = 58080 USER = "tester" PASSWD = "test123" REALM = "TestRealm" -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu Nov 24 02:46:56 2011 From: jython-checkins at python.org (philip.jenvey) Date: Thu, 24 Nov 2011 02:46:56 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=282=2E5=29=3A_fix_integration_?= =?utf8?q?of_Java_objects_w/_a_toString_method_returning_null?= Message-ID: http://hg.python.org/jython/rev/4d0f8af76d2d changeset: 6281:4d0f8af76d2d branch: 2.5 user: Philip Jenvey date: Wed Nov 23 17:42:02 2011 -0800 summary: fix integration of Java objects w/ a toString method returning null fixes #1819 thanks agronholm files: Lib/test/test_java_integration.py | 9 ++++++++- NEWS | 1 + src/org/python/core/PyJavaType.java | 3 ++- tests/java/javatests/ProxyTests.java | 4 ++++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_java_integration.py b/Lib/test/test_java_integration.py --- a/Lib/test/test_java_integration.py +++ b/Lib/test/test_java_integration.py @@ -25,7 +25,7 @@ from org.python.tests import (BeanImplementation, Child, Child2, CustomizableMapHolder, Listenable, ToUnicode) from org.python.tests.mro import (ConfusedOnGetitemAdd, FirstPredefinedGetitem, GetitemAdder) -from javatests.ProxyTests import Person +from javatests.ProxyTests import NullToString, Person class InstantiationTest(unittest.TestCase): @@ -473,6 +473,13 @@ self.assertRaises(TypeError, __import__, "org.python.tests.mro.ConfusedOnImport") self.assertRaises(TypeError, GetitemAdder.addPostdefined) + def test_null_tostring(self): + # http://bugs.jython.org/issue1819 + nts = NullToString() + self.assertEqual(repr(nts), '') + self.assertEqual(str(nts), '') + self.assertEqual(unicode(nts), '') + def roundtrip_serialization(obj): """Returns a deep copy of an object, via serializing it diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ - [ 1804 ] _tcpsocket doesn't have 'type' and 'proto' attributes - [ 1809 ] socket.getaddrinfo sometimes returns an object that crashes in __str__ - [ 1811 ] Recursive import bug w/ SQLAlchemy 0.7.3 + - [ 1819 ] Incorrect handling of Java object toString methods returning null Jython 2.5.2 same as 2.5.2rc4 diff --git a/src/org/python/core/PyJavaType.java b/src/org/python/core/PyJavaType.java --- a/src/org/python/core/PyJavaType.java +++ b/src/org/python/core/PyJavaType.java @@ -593,7 +593,8 @@ addMethod(new PyBuiltinMethodNarrow("__repr__") { @Override public PyObject __call__() { - return Py.newString(self.getJavaProxy().toString()); + String toString = self.getJavaProxy().toString(); + return toString == null ? Py.EmptyString : Py.newString(toString); } }); addMethod(new PyBuiltinMethodNarrow("__unicode__") { diff --git a/tests/java/javatests/ProxyTests.java b/tests/java/javatests/ProxyTests.java --- a/tests/java/javatests/ProxyTests.java +++ b/tests/java/javatests/ProxyTests.java @@ -35,4 +35,8 @@ } } + public static class NullToString { + public String toString() { return null; } + } + } -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Thu Nov 24 02:46:56 2011 From: jython-checkins at python.org (philip.jenvey) Date: Thu, 24 Nov 2011 02:46:56 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf8?q?=3A_merge_with_2=2E5?= Message-ID: http://hg.python.org/jython/rev/8bc674cdfd2d changeset: 6282:8bc674cdfd2d parent: 6279:c280db0483aa parent: 6281:4d0f8af76d2d user: Philip Jenvey date: Wed Nov 23 17:44:41 2011 -0800 summary: merge with 2.5 files: Lib/test/test_java_integration.py | 9 ++++++++- Lib/test/test_socket.py | 2 +- Lib/test/test_urllib2_localnet.py | 2 +- NEWS | 1 + src/org/python/core/PyJavaType.java | 3 ++- tests/java/javatests/ProxyTests.java | 4 ++++ 6 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_java_integration.py b/Lib/test/test_java_integration.py --- a/Lib/test/test_java_integration.py +++ b/Lib/test/test_java_integration.py @@ -25,7 +25,7 @@ from org.python.tests import (BeanImplementation, Child, Child2, CustomizableMapHolder, Listenable, ToUnicode) from org.python.tests.mro import (ConfusedOnGetitemAdd, FirstPredefinedGetitem, GetitemAdder) -from javatests.ProxyTests import Person +from javatests.ProxyTests import NullToString, Person class InstantiationTest(unittest.TestCase): @@ -473,6 +473,13 @@ self.assertRaises(TypeError, __import__, "org.python.tests.mro.ConfusedOnImport") self.assertRaises(TypeError, GetitemAdder.addPostdefined) + def test_null_tostring(self): + # http://bugs.jython.org/issue1819 + nts = NullToString() + self.assertEqual(repr(nts), '') + self.assertEqual(str(nts), '') + self.assertEqual(unicode(nts), '') + def roundtrip_serialization(obj): """Returns a deep copy of an object, via serializing it 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 @@ -20,7 +20,7 @@ from weakref import proxy from StringIO import StringIO -PORT = 50007 +PORT = 50100 HOST = 'localhost' MSG = 'Michael Gilfix was here\n' EIGHT_BIT_MSG = 'Bh\xed Al\xe1in \xd3 Cinn\xe9ide anseo\n' diff --git a/Lib/test/test_urllib2_localnet.py b/Lib/test/test_urllib2_localnet.py --- a/Lib/test/test_urllib2_localnet.py +++ b/Lib/test/test_urllib2_localnet.py @@ -226,7 +226,7 @@ class ProxyAuthTests(unittest.TestCase): URL = "http://www.foo.com" - PORT = 8080 + PORT = 58080 USER = "tester" PASSWD = "test123" REALM = "TestRealm" diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ - [ 1804 ] _tcpsocket doesn't have 'type' and 'proto' attributes - [ 1809 ] socket.getaddrinfo sometimes returns an object that crashes in __str__ - [ 1811 ] Recursive import bug w/ SQLAlchemy 0.7.3 + - [ 1819 ] Incorrect handling of Java object toString methods returning null Jython 2.5.2 same as 2.5.2rc4 diff --git a/src/org/python/core/PyJavaType.java b/src/org/python/core/PyJavaType.java --- a/src/org/python/core/PyJavaType.java +++ b/src/org/python/core/PyJavaType.java @@ -593,7 +593,8 @@ addMethod(new PyBuiltinMethodNarrow("__repr__") { @Override public PyObject __call__() { - return Py.newString(self.getJavaProxy().toString()); + String toString = self.getJavaProxy().toString(); + return toString == null ? Py.EmptyString : Py.newString(toString); } }); addMethod(new PyBuiltinMethodNarrow("__unicode__") { diff --git a/tests/java/javatests/ProxyTests.java b/tests/java/javatests/ProxyTests.java --- a/tests/java/javatests/ProxyTests.java +++ b/tests/java/javatests/ProxyTests.java @@ -35,4 +35,8 @@ } } + public static class NullToString { + public String toString() { return null; } + } + } -- Repository URL: http://hg.python.org/jython From pjenvey at underboss.org Thu Nov 24 02:47:01 2011 From: pjenvey at underboss.org (Philip Jenvey) Date: Wed, 23 Nov 2011 17:47:01 -0800 Subject: [Jython-checkins] jython (2.5): enable tests to be run from Jenkins on Fedora by changing port numbers In-Reply-To: References: Message-ID: On Nov 22, 2011, at 10:29 PM, oti.humbel wrote: > http://hg.python.org/jython/rev/8af641b6973e > changeset: 6280:8af641b6973e > branch: 2.5 > parent: 6278:ca6b9103ea54 > user: Oti Humbel > date: Wed Nov 23 07:28:40 2011 +0100 > summary: > enable tests to be run from Jenkins on Fedora by changing port numbers > Hey Oti, Don't forget to merge 2.5 branch commits to default, even if they don't belong there (in that case make an empty merge commit). I just merged this change so no need to do it for this one -- Philip Jenvey From jython-checkins at python.org Tue Nov 29 19:32:51 2011 From: jython-checkins at python.org (philip.jenvey) Date: Tue, 29 Nov 2011 19:32:51 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=282=2E5=29=3A_fix_os=2Elink_si?= =?utf8?q?lently_failing?= Message-ID: http://hg.python.org/jython/rev/ef1eb2bc70f5 changeset: 6283:ef1eb2bc70f5 branch: 2.5 parent: 6281:4d0f8af76d2d user: Philip Jenvey date: Tue Nov 29 10:31:53 2011 -0800 summary: fix os.link silently failing files: Lib/test/test_os_jy.py | 5 +++++ NEWS | 1 + src/org/python/modules/posix/PosixModule.java | 4 +++- 3 files changed, 9 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_os_jy.py b/Lib/test/test_os_jy.py --- a/Lib/test/test_os_jy.py +++ b/Lib/test/test_os_jy.py @@ -22,6 +22,11 @@ os.remove(test_support.TESTFN) self.assertRaises(OSError, os.utime, test_support.TESTFN, None) + def test_issue1824(self): + os.remove(test_support.TESTFN) + self.assertRaises(OSError, os.link, + test_support.TESTFN, test_support.TESTFN) + def test_main(): test_support.run_unittest(OSTestCase) diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -11,6 +11,7 @@ - [ 1809 ] socket.getaddrinfo sometimes returns an object that crashes in __str__ - [ 1811 ] Recursive import bug w/ SQLAlchemy 0.7.3 - [ 1819 ] Incorrect handling of Java object toString methods returning null + - [ 1824 ] os.link() can silently fail Jython 2.5.2 same as 2.5.2rc4 diff --git a/src/org/python/modules/posix/PosixModule.java b/src/org/python/modules/posix/PosixModule.java --- a/src/org/python/modules/posix/PosixModule.java +++ b/src/org/python/modules/posix/PosixModule.java @@ -464,7 +464,9 @@ "Create a hard link to a file."); @Hide(OS.NT) public static void link(String src, String dst) { - posix.link(absolutePath(src), absolutePath(dst)); + if (posix.link(absolutePath(src), absolutePath(dst)) < 0) { + throw errorFromErrno(); + } } public static PyString __doc__listdir = new PyString( -- Repository URL: http://hg.python.org/jython From jython-checkins at python.org Tue Nov 29 19:32:51 2011 From: jython-checkins at python.org (philip.jenvey) Date: Tue, 29 Nov 2011 19:32:51 +0100 Subject: [Jython-checkins] =?utf8?q?jython_=28merge_2=2E5_-=3E_default=29?= =?utf8?q?=3A_merge_w/_2=2E5?= Message-ID: http://hg.python.org/jython/rev/99821530273c changeset: 6284:99821530273c parent: 6282:8bc674cdfd2d parent: 6283:ef1eb2bc70f5 user: Philip Jenvey date: Tue Nov 29 10:32:28 2011 -0800 summary: merge w/ 2.5 files: Lib/test/test_os_jy.py | 5 +++++ NEWS | 1 + src/org/python/modules/posix/PosixModule.java | 4 +++- 3 files changed, 9 insertions(+), 1 deletions(-) diff --git a/Lib/test/test_os_jy.py b/Lib/test/test_os_jy.py --- a/Lib/test/test_os_jy.py +++ b/Lib/test/test_os_jy.py @@ -22,6 +22,11 @@ os.remove(test_support.TESTFN) self.assertRaises(OSError, os.utime, test_support.TESTFN, None) + def test_issue1824(self): + os.remove(test_support.TESTFN) + self.assertRaises(OSError, os.link, + test_support.TESTFN, test_support.TESTFN) + def test_main(): test_support.run_unittest(OSTestCase) diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -11,6 +11,7 @@ - [ 1809 ] socket.getaddrinfo sometimes returns an object that crashes in __str__ - [ 1811 ] Recursive import bug w/ SQLAlchemy 0.7.3 - [ 1819 ] Incorrect handling of Java object toString methods returning null + - [ 1824 ] os.link() can silently fail Jython 2.5.2 same as 2.5.2rc4 diff --git a/src/org/python/modules/posix/PosixModule.java b/src/org/python/modules/posix/PosixModule.java --- a/src/org/python/modules/posix/PosixModule.java +++ b/src/org/python/modules/posix/PosixModule.java @@ -464,7 +464,9 @@ "Create a hard link to a file."); @Hide(OS.NT) public static void link(String src, String dst) { - posix.link(absolutePath(src), absolutePath(dst)); + if (posix.link(absolutePath(src), absolutePath(dst)) < 0) { + throw errorFromErrno(); + } } public static PyString __doc__listdir = new PyString( -- Repository URL: http://hg.python.org/jython