Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
45ea3c6
bits method and test_bits
ewosborne Feb 11, 2018
b470a10
Cleaned up assert string
ewosborne Feb 11, 2018
4c8af71
blurb
ewosborne Feb 11, 2018
d1f9732
added docstring
ewosborne Feb 11, 2018
b72330d
Faster method, per Eric Smith
ewosborne Feb 12, 2018
6c7d1a4
redoing as __format__
ewosborne Feb 13, 2018
6a1d467
added ipv6 method
ewosborne Feb 13, 2018
41f637a
test cases and cleanup
ewosborne Feb 13, 2018
eddcc39
updated news
ewosborne Feb 13, 2018
eabf6d9
cleanup and NEWS.d
ewosborne Feb 13, 2018
ba05386
cleaned up old NEWS
ewosborne Feb 13, 2018
2bd9f60
removed cut and paste leftover
ewosborne Feb 15, 2018
d865609
one more cleanup
ewosborne Feb 15, 2018
798af6e
moved to regexp, moved away from v4- and v6-specific versions of __fo…
ewosborne Mar 4, 2018
db383e1
More cleanup, added ipv6 test cases
ewosborne Mar 4, 2018
b433282
more cleanup
ewosborne Mar 4, 2018
41109cc
more cleanup
ewosborne Mar 4, 2018
861b5e4
cleanup
ewosborne Mar 4, 2018
398b2c7
cleanup
ewosborne Mar 5, 2018
0274c0f
cleanup per review, part 1
ewosborne Sep 12, 2018
6d5fbdb
addressed review comments around help string and regexp matching
ewosborne Sep 12, 2018
d9061d2
wrapped v6 test strings. contiguous integers: break at 72char. with u…
ewosborne Sep 13, 2018
1401142
's' and '' tests for pv4 and ipv6
ewosborne Sep 13, 2018
acc1f06
whitespace cleanup
ewosborne Sep 15, 2018
8354756
Remove trailing whitespace
zware Sep 9, 2019
aaaa45c
Remove more trailing whitespace
zware Sep 11, 2019
8e1d9ca
Remove an excess blank line
zware Sep 11, 2019
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions Lib/ipaddress.py
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,43 @@ class _BaseV4:
def _explode_shorthand_ip_string(self):
return str(self)

def __format__(self, fmt):
"""Format: ends with b for bin, h for hex, n for native (bin for ipv4,
hex for ipv6). Takes # to include 0b, and _ as a separator
"""
if len(fmt) and fmt[-1] in 'bnx':
fmt_base = fmt[-1]
if fmt_base == 'n':
fmt_base = 'b' # binary is default for ipv4

if '_' in fmt:
separator = '_'
else:
separator = ''

# binary
if fmt_base == 'b':
# resulting string is '0b' + 32 bits
# plus 7 _ if needed
padlen = IPV4LENGTH+2 + (7*len(separator))

# hex
elif fmt_base == 'x':
# resulting string is '0x' + 8 hex digits
# plus a single _ if needed
padlen = int(IPV4LENGTH/4)+2 + len(separator)

retstr = f'{int(self):#0{padlen}{separator}{fmt_base}}'

# strip left two if necessary
if '#' not in fmt:
retstr = retstr[2:]

else:
retstr = str(self)

return retstr

@classmethod
def _make_netmask(cls, arg):
"""Make a (netmask, prefix_len) tuple from the given argument.
Expand Down Expand Up @@ -1627,6 +1664,43 @@ class _BaseV6:
# when constructed (see _make_netmask()).
_netmask_cache = {}

def __format__(self, fmt):
"""Format: ends with b for bin, h for hex, n for native (bin for ipv4,
hex for ipv6). Takes # to include 0b, and _ as a separator
"""
if len(fmt) and fmt[-1] in 'bnx':
fmt_base = fmt[-1]
if fmt_base == 'n':
fmt_base = 'x' # hex is default for ipv4

if '_' in fmt:
separator = '_'
else:
separator = ''

# binary
if fmt_base == 'b':
# resulting string is '0b' + 32 bits
# plus 7 _ if needed
padlen = IPV6LENGTH+2 + (31*len(separator))

# hex
elif fmt_base == 'x':
# resulting string is '0x' + 8 hex digits
# plus 7 single _ if needed
padlen = int(IPV6LENGTH/4)+2 + (7*len(separator))

retstr = f'{int(self):#0{padlen}{separator}{fmt_base}}'

# strip left two if necessary
if '#' not in fmt:
retstr = retstr[2:]

else:
retstr = str(self)

return retstr

@classmethod
def _make_netmask(cls, arg):
"""Make a (netmask, prefix_len) tuple from the given argument.
Expand Down
56 changes: 56 additions & 0 deletions Lib/test/test_ipaddress.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,27 @@ def assertBadLength(length):
class AddressTestCase_v4(BaseTestCase, CommonTestMixin_v4):
factory = ipaddress.IPv4Address

def test_format(self):
addr = ipaddress.IPv4Address("1.2.3.4")
pairs = [
("b" ,"00000001000000100000001100000100"),
("n" ,"00000001000000100000001100000100"),
("x" ,"01020304"),
("_b" ,"0000_0001_0000_0010_0000_0011_0000_0100"),
("_n" ,"0000_0001_0000_0010_0000_0011_0000_0100"),
("_x" ,"0102_0304"),
("#b" ,"0b00000001000000100000001100000100"),
("#n" ,"0b00000001000000100000001100000100"),
("#x" ,"0x01020304"),
("#_b" ,"0b0000_0001_0000_0010_0000_0011_0000_0100"),
("#_n" ,"0b0000_0001_0000_0010_0000_0011_0000_0100"),
("#_x" ,"0x0102_0304"),
]
for (fmt, txt) in pairs:
res = format(addr, fmt)
self.assertEqual(txt, format(addr, fmt))


def test_network_passed_as_address(self):
addr = "127.0.0.1/24"
with self.assertAddressError("Unexpected '/' in %r", addr):
Expand Down Expand Up @@ -266,6 +287,41 @@ def test_weakref(self):
class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6):
factory = ipaddress.IPv6Address

def test_format(self):
addr = ipaddress.IPv6Address("::1.2.3.4")
pairs = [
("b"
,"000000000000000000000000000000000"
"000000000000000000000000000000000"
"00000000000000000000000000000000"
"000001000000100000001100000100"),
("n" ,"00000000000000000000000001020304"),
("x" ,"00000000000000000000000001020304"),
("_b"
,"0000_0000_0000_0000_0000_0000_0000_0000_"
"0000_0000_0000_0000_0000_0000_0000_0000_"
"0000_0000_0000_0000_0000_0000_0000_0000_"
"0000_0001_0000_0010_0000_0011_0000_0100"),
("_n" ,"0000_0000_0000_0000_0000_0000_0102_0304"),
("_x" ,"0000_0000_0000_0000_0000_0000_0102_0304"),
("#b"
,"0b000000000000000000000000000000000"
"000000000000000000000000000000000"
"000000000000000000000000000000000"
"00001000000100000001100000100"),
("#n" ,"0x00000000000000000000000001020304"),
("#x" ,"0x00000000000000000000000001020304"),
("#_b"
,"0b0000_0000_0000_0000_0000_0000_0000_0000_"
"0000_0000_0000_0000_0000_0000_0000_0000_"
"0000_0000_0000_0000_0000_0000_0000_0000_"
"0000_0001_0000_0010_0000_0011_0000_0100"),
("#_n" ,"0x0000_0000_0000_0000_0000_0000_0102_0304"),
("#_x" ,"0x0000_0000_0000_0000_0000_0000_0102_0304"),
]
for (fmt, txt) in pairs:
res = format(addr, fmt)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing an assertion here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed - remove the whole 'res = ...' line. Cut and paste leftover.


def test_network_passed_as_address(self):
addr = "::1/24"
with self.assertAddressError("Unexpected '/' in %r", addr):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Added __format__ to IPv4 and IPv6 classes. Always outputs a fully zero-
padded string. Supports b/x/n modifiers (bin/hex/native format). Native
format for IPv4 is bin, native format for IPv6 is hex. Also supports '#' and
'_' modifiers.