RosettaCodeData/Task/Parse-an-IP-Address/Python/parse-an-ip-address-2.py

125 lines
4.8 KiB
Python

import string
from pyparsing import * # import antigravity
tests="""#
127.0.0.1 # The "localhost" IPv4 address
127.0.0.1:80 # The "localhost" IPv4 address, with a specified port (80)
::1 # The "localhost" IPv6 address
[::1]:80 # The "localhost" IPv6 address, with a specified port (80)
2605:2700:0:3::4713:93e3 # Rosetta Code's primary server's public IPv6 address
[2605:2700:0:3::4713:93e3]:80 # Rosetta Code's primary server's public IPv6 address, +port (80)
2001:db8:85a3:0:0:8a2e:370:7334 # doc, IPv6 for 555-1234
2001:db8:85a3::8a2e:370:7334 # doc
[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443 # doc +port
192.168.0.1 # private
::ffff:192.168.0.1 # private transitional
::ffff:71.19.147.227 # Rosetta Code's transitional
[::ffff:71.19.147.227]:80 # Rosetta Code's transitional +port
:: # unspecified
256.0.0.0 # invalid, octet > 255 (currently not detected)
g::1 # invalid
0000 Bad address
0000:0000 Bad address
0000:0000:0000:0000:0000:0000:0000:0000 Good address
0000:0000:0000::0000:0000 Good Address
0000::0000::0000:0000 Bad address
ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff Good address
ffff:ffff:ffff:fffg:ffff:ffff:ffff:ffff Bad address
fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff Good address
fff:ffff:0:ffff:ffff:ffff:ffff:ffff Good address
"""
def print_args(args):
print "print_args:", args
def join(args):
args[0]="".join(args)
del args[1:]
def replace(val):
def lambda_replace(args):
args[0]=val
del args[1:]
return lambda_replace
def atoi(args): args[0]=string.atoi(args[0])
def itohex2(args): args[0]="%02x"%args[0]
def hextoi(args): args[0]=string.atoi(args[0], 16)
def itohex4(args): args[0]="%04x"%args[0]
def assert_in_range(lwb, upb):
def range_check(args):
return # turn range checking off
if args[0] < lwb:
raise ValueError,"value %d < %d"%(args[0], lwb)
if args[0] > upb:
raise ValueError,"value %d > %d"%(args[0], upb)
return range_check
dot = Literal(".").suppress()("dot"); colon = Literal(":").suppress()("colon")
octet = Word(nums).setParseAction(atoi,assert_in_range(0,255),itohex2)("octet");
port = Word(nums).setParseAction(atoi,assert_in_range(0,256*256-1))("port")
ipv4 = (octet + (dot+octet)*3)("addr")
ipv4.setParseAction(join) #,hextoi)
ipv4_port = ipv4+colon.suppress()+port
a2f = "abcdef"
hex = oneOf(" ".join(nums+a2f));
hexet = (hex*(0,4))("hexet")
hexet.setParseAction(join, hextoi, itohex4)
max=8; stop=max+1
xXXXX_etc = [None, hexet]; xXXXX_etc.extend([hexet + (colon+hexet)*n for n in range(1,max)])
x0000_etc = [ Literal("::").setParseAction(replace("0000"*num_x0000s)) for num_x0000s in range(stop) ]
ipv6=xXXXX_etc[-1]+x0000_etc[0] | xXXXX_etc[-1]
# Build a table of rules for IPv6, in particular the double colon
for num_prefix in range(max-1, -1, -1):
for num_x0000s in range(0,stop-num_prefix):
x0000 = x0000_etc[num_x0000s]
num_suffix=max-num_prefix-num_x0000s
if num_prefix:
if num_suffix: pat = xXXXX_etc[num_prefix]+x0000+xXXXX_etc[num_suffix]
else: pat = xXXXX_etc[num_prefix]+x0000
elif num_suffix: pat = x0000+xXXXX_etc[num_suffix]
else: pat=x0000
ipv6 = ipv6 | pat
ipv6.setParseAction(join) # ,hextoi)
ipv6_port = Literal("[").suppress() + ipv6 + Literal("]").suppress()+colon+port
ipv6_transitional = (Literal("::ffff:").setParseAction(replace("0"*20+"ffff"))+ipv4).setParseAction(join)
ipv6_transitional_port = Literal("[").suppress() + ipv6_transitional + Literal("]").suppress()+colon+port
ip_fmt = (
(ipv4_port|ipv4)("ipv4") |
(ipv6_transitional_port|ipv6_transitional|ipv6_port|ipv6)("ipv6")
) + LineEnd()
class IPAddr(object):
def __init__(self, string):
self.service = dict(zip(("address","port"), ip_fmt.parseString(string)[:]))
def __getitem__(self, key): return self.service[key]
def __contains__(self, key): return key in self.service
def __repr__(self): return `self.service` # "".join(self.service)
address=property(lambda self: self.service["address"])
port=property(lambda self: self.service["port"])
is_service=property(lambda self: "port" in self.service)
version=property(lambda self: {False:4, True:6}[len(self.address)>8])
for test in tests.splitlines():
if not test.startswith("#"):
ip_str, desc = test.split(None,1)
print ip_str,"=>",
try:
ip=IPAddr(ip_str)
print ip, "IP Version:",ip.version,"- Address is OK!",
except (ParseException,ValueError), details: print "Bad! IP address syntax error detected:",details,
print "- Actually:",desc