From 3407d7679d063d61647dfb80a5848a0f255e9fc5 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sun, 31 Oct 2021 16:06:30 +0000 Subject: [PATCH 1/9] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst diff --git a/Misc/NEWS.d/next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst b/Misc/NEWS.d/next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst new file mode 100644 index 00000000000000..34d1a64fa6ffe1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst @@ -0,0 +1,2 @@ +Improve the textual representation of IPv4-mapped IPv6 addresses to :mod:`ipaddress`. Patch by Oleksandr +Pavliuk. \ No newline at end of file From 118e335dd1019d5c6ee0caf19ae7b813eeaa8572 Mon Sep 17 00:00:00 2001 From: Olexandr Pavlyuk Date: Thu, 13 Apr 2023 12:59:33 +0200 Subject: [PATCH 2/9] bpo-43633 Improve the textual representation of IPv4-mapped IPv6 addresses Represent IPv4-mapped IPv6 address as x:x:x:x:x:x:d.d.d.d, where the 'x's are the hexadecimal values of the six high-order 16-bit pieces of the address, and the 'd's are the decimal values of the four low-order 8-bit pieces of the address (standard IPv4 representation). --- Lib/ipaddress.py | 34 ++++++++++++++++++- Lib/test/test_ipaddress.py | 7 ++++ .../2021-10-31-16-06-28.bpo-43633.vflwXv.rst | 3 +- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py index e601f6f476e4df..1a4fd0fbceb540 100644 --- a/Lib/ipaddress.py +++ b/Lib/ipaddress.py @@ -1918,8 +1918,40 @@ def __init__(self, address): self._ip = self._ip_int_from_string(addr_str) + def _explode_shorthand_ip_string(self): + ipv4_mapped = self.ipv4_mapped + if ipv4_mapped is None: + long_form = super()._explode_shorthand_ip_string() + else: + prefix_len = 30 + raw_exploded_str = super()._explode_shorthand_ip_string() + long_form = "%s%s" % (raw_exploded_str[:prefix_len], str(ipv4_mapped)) + return long_form + + def _ipv4_mapped_ipv6_to_str(self): + """Return convenient text representation of IPv4-mapped IPv6 address + + See RFC 4291 2.5.5.2, 2.2 p.3 for details. + + Returns: + A string, 'x:x:x:x:x:x:d.d.d.d', where the 'x's are the hexadecimal values of + the six high-order 16-bit pieces of the address, and the 'd's are + the decimal values of the four low-order 8-bit pieces of the + address (standard IPv4 representation) as defined in RFC 4291 2.2 p.3. + + """ + ipv4_mapped = self.ipv4_mapped + if ipv4_mapped is None: + raise AddressValueError("Can not apply to non-IPv4-mapped IPv6 address %s" % str(self)) + high_order_bits = self._ip >> 32 + return "%s:%s" % (self._string_from_ip_int(high_order_bits), str(ipv4_mapped)) + def __str__(self): - ip_str = super().__str__() + ipv4_mapped = self.ipv4_mapped + if ipv4_mapped is None: + ip_str = super().__str__() + else: + ip_str = self._ipv4_mapped_ipv6_to_str() return ip_str + '%' + self._scope_id if self._scope_id else ip_str def __hash__(self): diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index b0605f0be04bb4..d9c0763c3a55d7 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -1297,6 +1297,13 @@ def testGetIp(self): self.assertEqual(str(self.ipv6_scoped_interface.ip), '2001:658:22a:cafe:200::1') + def testIPv6IPv4MappedStringRepresentation(self): + ipv6_ipv4_mapped_str = '::ffff:1.2.3.4' + ipv6_ipv4_mapped_address = ipaddress.IPv6Address(ipv6_ipv4_mapped_str) + ipv6_ipv4_mapped_interface = ipaddress.IPv6Interface(ipv6_ipv4_mapped_str) + self.assertEqual(str(ipv6_ipv4_mapped_address), ipv6_ipv4_mapped_str) + self.assertEqual(str(ipv6_ipv4_mapped_interface.ip), ipv6_ipv4_mapped_str) + def testGetScopeId(self): self.assertEqual(self.ipv6_address.scope_id, None) diff --git a/Misc/NEWS.d/next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst b/Misc/NEWS.d/next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst index 34d1a64fa6ffe1..5e8f5235609880 100644 --- a/Misc/NEWS.d/next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst +++ b/Misc/NEWS.d/next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst @@ -1,2 +1 @@ -Improve the textual representation of IPv4-mapped IPv6 addresses to :mod:`ipaddress`. Patch by Oleksandr -Pavliuk. \ No newline at end of file +Improve the textual representation of IPv4-mapped IPv6 addresses (:rfc:`4291` Sections 2.2, 2.5.5.2) in :mod:`ipaddress.IPv6Address`. Patch by Oleksandr Pavliuk. From f3642caa4f27feb4e8fb19b35989f16a9316afa9 Mon Sep 17 00:00:00 2001 From: Olexandr Pavlyuk Date: Thu, 13 Apr 2023 13:00:01 +0200 Subject: [PATCH 3/9] bpo-43633 Update tests --- Lib/test/test_ipaddress.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index d9c0763c3a55d7..0eefba33895ab3 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -1298,10 +1298,14 @@ def testGetIp(self): '2001:658:22a:cafe:200::1') def testIPv6IPv4MappedStringRepresentation(self): - ipv6_ipv4_mapped_str = '::ffff:1.2.3.4' + long_prefix_len = 30 + prefix = '::ffff:' + ipv4 = '1.2.3.4' + ipv6_ipv4_mapped_str = '%s%s' % (prefix, ipv4) ipv6_ipv4_mapped_address = ipaddress.IPv6Address(ipv6_ipv4_mapped_str) ipv6_ipv4_mapped_interface = ipaddress.IPv6Interface(ipv6_ipv4_mapped_str) self.assertEqual(str(ipv6_ipv4_mapped_address), ipv6_ipv4_mapped_str) + self.assertEqual(ipv6_ipv4_mapped_address.exploded, ipv6_ipv4_mapped_str.exploded[long_prefix_len] + ipv4) self.assertEqual(str(ipv6_ipv4_mapped_interface.ip), ipv6_ipv4_mapped_str) def testGetScopeId(self): From 669cc90be9b00ecbc45d8fedaaf1ff36bdc5ec3d Mon Sep 17 00:00:00 2001 From: Olexandr Pavlyuk Date: Thu, 13 Apr 2023 13:00:08 +0200 Subject: [PATCH 4/9] bpo-43633 Fix tests --- Lib/test/test_ipaddress.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index 0eefba33895ab3..39ec16bd1ab200 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -1298,14 +1298,14 @@ def testGetIp(self): '2001:658:22a:cafe:200::1') def testIPv6IPv4MappedStringRepresentation(self): - long_prefix_len = 30 - prefix = '::ffff:' + long_prefix = '0000:0000:0000:0000:0000:ffff:' + short_prefix = '::ffff:' ipv4 = '1.2.3.4' - ipv6_ipv4_mapped_str = '%s%s' % (prefix, ipv4) + ipv6_ipv4_mapped_str = '%s%s' % (short_prefix, ipv4) ipv6_ipv4_mapped_address = ipaddress.IPv6Address(ipv6_ipv4_mapped_str) ipv6_ipv4_mapped_interface = ipaddress.IPv6Interface(ipv6_ipv4_mapped_str) self.assertEqual(str(ipv6_ipv4_mapped_address), ipv6_ipv4_mapped_str) - self.assertEqual(ipv6_ipv4_mapped_address.exploded, ipv6_ipv4_mapped_str.exploded[long_prefix_len] + ipv4) + self.assertEqual(ipv6_ipv4_mapped_address.exploded, long_prefix + ipv4) self.assertEqual(str(ipv6_ipv4_mapped_interface.ip), ipv6_ipv4_mapped_str) def testGetScopeId(self): From c2842ceec9837eb2ddefe183889e2eeff100789a Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Mon, 31 Jul 2023 13:19:11 +0200 Subject: [PATCH 5/9] Add failed test --- Lib/test/test_ipaddress.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index da8ae44cea682a..704a38ea522c0c 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -1323,14 +1323,15 @@ def testGetIp(self): def testIPv6IPv4MappedStringRepresentation(self): long_prefix = '0000:0000:0000:0000:0000:ffff:' - short_prefix = '::ffff:' ipv4 = '1.2.3.4' - ipv6_ipv4_mapped_str = '%s%s' % (short_prefix, ipv4) - ipv6_ipv4_mapped_address = ipaddress.IPv6Address(ipv6_ipv4_mapped_str) - ipv6_ipv4_mapped_interface = ipaddress.IPv6Interface(ipv6_ipv4_mapped_str) - self.assertEqual(str(ipv6_ipv4_mapped_address), ipv6_ipv4_mapped_str) - self.assertEqual(ipv6_ipv4_mapped_address.exploded, long_prefix + ipv4) - self.assertEqual(str(ipv6_ipv4_mapped_interface.ip), ipv6_ipv4_mapped_str) + for short_prefix in ('::ffff:', '::'): + ipv6_ipv4_mapped_str = '%s%s' % (short_prefix, ipv4) + with self.subTest(addr=ipv6_ipv4_mapped_str): + ipv6_ipv4_mapped_address = ipaddress.IPv6Address(ipv6_ipv4_mapped_str) + ipv6_ipv4_mapped_interface = ipaddress.IPv6Interface(ipv6_ipv4_mapped_str) + self.assertEqual(str(ipv6_ipv4_mapped_address), ipv6_ipv4_mapped_str) + self.assertEqual(ipv6_ipv4_mapped_address.exploded, long_prefix + ipv4) + self.assertEqual(str(ipv6_ipv4_mapped_interface.ip), ipv6_ipv4_mapped_str) def testGetScopeId(self): self.assertEqual(self.ipv6_address.scope_id, From 97bd01cabc4a1d054beb9d0e355217f8c505a2da Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Mon, 31 Jul 2023 14:39:19 +0200 Subject: [PATCH 6/9] User shorter variable names --- Lib/test/test_ipaddress.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index 704a38ea522c0c..bdb4eb1486aff0 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -1325,13 +1325,13 @@ def testIPv6IPv4MappedStringRepresentation(self): long_prefix = '0000:0000:0000:0000:0000:ffff:' ipv4 = '1.2.3.4' for short_prefix in ('::ffff:', '::'): - ipv6_ipv4_mapped_str = '%s%s' % (short_prefix, ipv4) - with self.subTest(addr=ipv6_ipv4_mapped_str): - ipv6_ipv4_mapped_address = ipaddress.IPv6Address(ipv6_ipv4_mapped_str) - ipv6_ipv4_mapped_interface = ipaddress.IPv6Interface(ipv6_ipv4_mapped_str) - self.assertEqual(str(ipv6_ipv4_mapped_address), ipv6_ipv4_mapped_str) - self.assertEqual(ipv6_ipv4_mapped_address.exploded, long_prefix + ipv4) - self.assertEqual(str(ipv6_ipv4_mapped_interface.ip), ipv6_ipv4_mapped_str) + ipv6_ipv4_str = '%s%s' % (short_prefix, ipv4) + with self.subTest(addr=ipv6_ipv4_str): + ipv6_ipv4_addr = ipaddress.IPv6Address(ipv6_ipv4_str) + ipv6_ipv4_iface = ipaddress.IPv6Interface(ipv6_ipv4_str) + self.assertEqual(str(ipv6_ipv4_addr), ipv6_ipv4_str) + self.assertEqual(ipv6_ipv4_addr.exploded, long_prefix + ipv4) + self.assertEqual(str(ipv6_ipv4_iface.ip), ipv6_ipv4_str) def testGetScopeId(self): self.assertEqual(self.ipv6_address.scope_id, From 2d2deeaa9bc24cdd2b5c5397dd26d7c0e6ffc83b Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Mon, 31 Jul 2023 14:51:36 +0200 Subject: [PATCH 7/9] Revert test back --- Lib/test/test_ipaddress.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py index bdb4eb1486aff0..6f204948c9fc48 100644 --- a/Lib/test/test_ipaddress.py +++ b/Lib/test/test_ipaddress.py @@ -1323,15 +1323,14 @@ def testGetIp(self): def testIPv6IPv4MappedStringRepresentation(self): long_prefix = '0000:0000:0000:0000:0000:ffff:' + short_prefix = '::ffff:' ipv4 = '1.2.3.4' - for short_prefix in ('::ffff:', '::'): - ipv6_ipv4_str = '%s%s' % (short_prefix, ipv4) - with self.subTest(addr=ipv6_ipv4_str): - ipv6_ipv4_addr = ipaddress.IPv6Address(ipv6_ipv4_str) - ipv6_ipv4_iface = ipaddress.IPv6Interface(ipv6_ipv4_str) - self.assertEqual(str(ipv6_ipv4_addr), ipv6_ipv4_str) - self.assertEqual(ipv6_ipv4_addr.exploded, long_prefix + ipv4) - self.assertEqual(str(ipv6_ipv4_iface.ip), ipv6_ipv4_str) + ipv6_ipv4_str = short_prefix + ipv4 + ipv6_ipv4_addr = ipaddress.IPv6Address(ipv6_ipv4_str) + ipv6_ipv4_iface = ipaddress.IPv6Interface(ipv6_ipv4_str) + self.assertEqual(str(ipv6_ipv4_addr), ipv6_ipv4_str) + self.assertEqual(ipv6_ipv4_addr.exploded, long_prefix + ipv4) + self.assertEqual(str(ipv6_ipv4_iface.ip), ipv6_ipv4_str) def testGetScopeId(self): self.assertEqual(self.ipv6_address.scope_id, From e8a7015f43a33a8a02facad1872ac82d6d2518ab Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Mon, 31 Jul 2023 15:28:59 +0200 Subject: [PATCH 8/9] Update 2021-10-31-16-06-28.bpo-43633.vflwXv.rst --- .../next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst b/Misc/NEWS.d/next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst index 5e8f5235609880..b0a929eefbd800 100644 --- a/Misc/NEWS.d/next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst +++ b/Misc/NEWS.d/next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst @@ -1 +1 @@ -Improve the textual representation of IPv4-mapped IPv6 addresses (:rfc:`4291` Sections 2.2, 2.5.5.2) in :mod:`ipaddress.IPv6Address`. Patch by Oleksandr Pavliuk. +Improve the textual representation of IPv4-mapped IPv6 addresses (:rfc:`4291` Sections 2.2, 2.5.5.2) in :class:`ipaddress.IPv6Address`. Patch by Oleksandr Pavliuk. From d74759b0f3194f48d4548173d0cd20d678ecd568 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Mon, 31 Jul 2023 15:29:21 +0200 Subject: [PATCH 9/9] Update 2021-10-31-16-06-28.bpo-43633.vflwXv.rst --- .../next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst b/Misc/NEWS.d/next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst index b0a929eefbd800..025de1e1a7d6ef 100644 --- a/Misc/NEWS.d/next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst +++ b/Misc/NEWS.d/next/Library/2021-10-31-16-06-28.bpo-43633.vflwXv.rst @@ -1 +1 @@ -Improve the textual representation of IPv4-mapped IPv6 addresses (:rfc:`4291` Sections 2.2, 2.5.5.2) in :class:`ipaddress.IPv6Address`. Patch by Oleksandr Pavliuk. +Improve the textual representation of IPv4-mapped IPv6 addresses (:rfc:`4291` Sections 2.2, 2.5.5.2) in :mod:`ipaddress`. Patch by Oleksandr Pavliuk.