|
@@ -1,4 +1,5 @@
|
|
|
import re
|
|
|
+from typing import Union, Any, Optional
|
|
|
|
|
|
|
|
|
class IPAddress:
|
|
@@ -13,7 +14,7 @@ class IPAddress:
|
|
|
# 4 numbers between 0 and 255, joined together with dots
|
|
|
IP_REGEXP = r"{0}\.{0}\.{0}\.{0}".format(_IP_NUMBER_REGEXP)
|
|
|
|
|
|
- def __init__(self, intlist):
|
|
|
+ def __init__(self, intlist: list[int]) -> "IPAddress":
|
|
|
"""
|
|
|
Construct an ipv4-address with a list of 4 integers, e.g. to construct the ip 10.0.0.0 pass [10, 0, 0, 0]
|
|
|
"""
|
|
@@ -26,7 +27,7 @@ class IPAddress:
|
|
|
self.ipnum = int.from_bytes(bytes(intlist), "big")
|
|
|
|
|
|
@staticmethod
|
|
|
- def parse(ip: str):
|
|
|
+ def parse(ip: str) -> "IPAddress":
|
|
|
"""
|
|
|
Parse an ip-address-string. If the string does not comply to the ipv4-format a ValueError is raised
|
|
|
:param ip: A string-representation of an ip-address, e.g. "10.0.0.0"
|
|
@@ -41,7 +42,7 @@ class IPAddress:
|
|
|
return IPAddress(numbers)
|
|
|
|
|
|
@staticmethod
|
|
|
- def from_int(numeric: int):
|
|
|
+ def from_int(numeric: int) -> "IPAddress":
|
|
|
if numeric not in range(1 << 32):
|
|
|
raise ValueError("numeric value must be in uint-range")
|
|
|
|
|
@@ -49,7 +50,7 @@ class IPAddress:
|
|
|
return IPAddress(list(numeric.to_bytes(4, "big")))
|
|
|
|
|
|
@staticmethod
|
|
|
- def is_ipv4(ip: str):
|
|
|
+ def is_ipv4(ip: str) -> bool:
|
|
|
"""
|
|
|
Check if the supplied string is in ipv4-format
|
|
|
"""
|
|
@@ -57,19 +58,19 @@ class IPAddress:
|
|
|
match = re.match("^" + IPAddress.IP_REGEXP + "$", ip)
|
|
|
return True if match else False
|
|
|
|
|
|
- def to_int(self):
|
|
|
+ def to_int(self) -> int:
|
|
|
"""
|
|
|
Convert the ip-address to a 32-bit uint, e.g. IPAddress.parse("10.0.0.255").to_int() returns 0x0a0000ff
|
|
|
"""
|
|
|
return self.ipnum
|
|
|
|
|
|
- def is_private(self):
|
|
|
+ def is_private(self) -> bool:
|
|
|
"""
|
|
|
Returns a boolean indicating if the ip-address lies in the private ip-segments (see ReservedIPBlocks)
|
|
|
"""
|
|
|
return ReservedIPBlocks.is_private(self)
|
|
|
|
|
|
- def get_private_segment(self):
|
|
|
+ def get_private_segment(self) -> bool:
|
|
|
"""
|
|
|
Return the private ip-segment the ip-address belongs to (there are several)
|
|
|
If this ip does not belong to a private ip-segment a ValueError is raised
|
|
@@ -77,55 +78,55 @@ class IPAddress:
|
|
|
"""
|
|
|
return ReservedIPBlocks.get_private_segment(self)
|
|
|
|
|
|
- def is_localhost(self):
|
|
|
+ def is_localhost(self) -> bool:
|
|
|
"""
|
|
|
Returns a boolean indicating if the ip-address lies in the localhost-segment
|
|
|
"""
|
|
|
return ReservedIPBlocks.is_localhost(self)
|
|
|
|
|
|
- def is_multicast(self):
|
|
|
+ def is_multicast(self) -> bool:
|
|
|
"""
|
|
|
Returns a boolean indicating if the ip-address lies in the multicast-segment
|
|
|
"""
|
|
|
return ReservedIPBlocks.is_multicast(self)
|
|
|
|
|
|
- def is_reserved(self):
|
|
|
+ def is_reserved(self) -> bool:
|
|
|
"""
|
|
|
Returns a boolean indicating if the ip-address lies in the reserved-segment
|
|
|
"""
|
|
|
return ReservedIPBlocks.is_reserved(self)
|
|
|
|
|
|
- def is_zero_conf(self):
|
|
|
+ def is_zero_conf(self) -> bool:
|
|
|
"""
|
|
|
Returns a boolean indicating if the ip-address lies in the zeroconf-segment
|
|
|
"""
|
|
|
return ReservedIPBlocks.is_zero_conf(self)
|
|
|
|
|
|
- def _tuple(self):
|
|
|
+ def _tuple(self) -> (int,int,int,int):
|
|
|
return tuple(self.ipnum.to_bytes(4, "big"))
|
|
|
|
|
|
- def __repr__(self):
|
|
|
+ def __repr__(self) -> str:
|
|
|
"""
|
|
|
Following the python style guide, eval(repr(obj)) should equal obj
|
|
|
"""
|
|
|
return "IPAddress([%i, %i, %i, %i])" % self._tuple()
|
|
|
|
|
|
- def __str__(self):
|
|
|
+ def __str__(self) -> str:
|
|
|
"""
|
|
|
Return the ip-address described by this object in ipv4-format
|
|
|
"""
|
|
|
return "%i.%i.%i.%i" % self._tuple()
|
|
|
|
|
|
- def __hash__(self):
|
|
|
+ def __hash__(self) -> int:
|
|
|
return self.ipnum
|
|
|
|
|
|
- def __eq__(self, other):
|
|
|
+ def __eq__(self, other: Any) -> bool:
|
|
|
if other is None:
|
|
|
return False
|
|
|
|
|
|
return isinstance(other, IPAddress) and self.ipnum == other.ipnum
|
|
|
|
|
|
- def __lt__(self, other):
|
|
|
+ def __lt__(self, other: Any) -> bool:
|
|
|
if other is None:
|
|
|
raise TypeError("Cannot compare to None")
|
|
|
if not isinstance(other, IPAddress):
|
|
@@ -133,7 +134,7 @@ class IPAddress:
|
|
|
|
|
|
return self.ipnum < other.ipnum
|
|
|
|
|
|
- def __int__(self):
|
|
|
+ def __int__(self) -> bool:
|
|
|
return self.ipnum
|
|
|
|
|
|
class IPAddressBlock:
|
|
@@ -146,7 +147,7 @@ class IPAddressBlock:
|
|
|
# this regex describes CIDR-notation (an ip-address plus "/XX", whereas XX is a number between 1 and 32)
|
|
|
CIDR_REGEXP = IPAddress.IP_REGEXP + r"(\/(3[0-2]|[12]?\d)|)?"
|
|
|
|
|
|
- def __init__(self, ip, netmask = 32):
|
|
|
+ def __init__(self, ip: Union(str, list, IPAddress), netmask = 32) -> "IPAddressBlock":
|
|
|
"""
|
|
|
Construct a ip-block given a ip-address and a netmask. Given an ip and a netmask,
|
|
|
the constructed ip-block will describe the range ip/netmask (e.g. 127.0.0.1/8)
|
|
@@ -165,7 +166,7 @@ class IPAddressBlock:
|
|
|
self.netmask = netmask
|
|
|
|
|
|
@staticmethod
|
|
|
- def parse(cidr: str):
|
|
|
+ def parse(cidr: str) -> "IPAddressBlock":
|
|
|
"""
|
|
|
Parse a string in cidr-notation and return a IPAddressBlock describing the ip-segment
|
|
|
If the string is not in cidr-notation a ValueError is raised
|
|
@@ -180,42 +181,42 @@ class IPAddressBlock:
|
|
|
|
|
|
return IPAddressBlock(ip, suffix)
|
|
|
|
|
|
- def block_size(self):
|
|
|
+ def block_size(self) -> int:
|
|
|
"""
|
|
|
Return the size of the ip-address-block. E.g. the size of someip/24 is 256
|
|
|
"""
|
|
|
return 2 ** (32 - self.netmask)
|
|
|
|
|
|
- def first_address(self):
|
|
|
+ def first_address(self) -> IPAddress:
|
|
|
"""
|
|
|
Return the first ip-address of the ip-block
|
|
|
"""
|
|
|
return IPAddress.from_int(self.ipnum)
|
|
|
|
|
|
- def last_address(self):
|
|
|
+ def last_address(self) -> IPAddress:
|
|
|
"""
|
|
|
Return the last ip-address of the ip-block
|
|
|
"""
|
|
|
return IPAddress.from_int(self.ipnum + self.block_size() - 1)
|
|
|
|
|
|
- def _bitmask(self, netmask):
|
|
|
+ def _bitmask(self, netmask: int) -> int:
|
|
|
ones = lambda x: (1 << x) - 1
|
|
|
|
|
|
return ones(32) ^ ones(32 - netmask)
|
|
|
|
|
|
- def __repr__(self):
|
|
|
+ def __repr__(self) -> str:
|
|
|
"""
|
|
|
Conforming to python style-guide, eval(repr(obj)) equals obj
|
|
|
"""
|
|
|
return "IPAddressBlock(%s, %i)" % (repr(IPAddress.from_int(self.ipnum)), self.netmask)
|
|
|
|
|
|
- def __str__(self):
|
|
|
+ def __str__(self) -> str:
|
|
|
"""
|
|
|
Return a string in cidr-notation
|
|
|
"""
|
|
|
return str(IPAddress.from_int(self.ipnum)) + "/" + str(self.netmask)
|
|
|
|
|
|
- def __contains__(self, ip):
|
|
|
+ def __contains__(self, ip: IPAddress) -> bool:
|
|
|
return (ip.to_int() & self._bitmask(self.netmask)) == self.ipnum
|
|
|
|
|
|
class ReservedIPBlocks:
|
|
@@ -239,11 +240,11 @@ class ReservedIPBlocks:
|
|
|
ZERO_CONF_SEGMENT = IPAddressBlock.parse("169.254.0.0/16")
|
|
|
|
|
|
@staticmethod
|
|
|
- def is_private(ip):
|
|
|
+ def is_private(ip: IPAddress) -> bool:
|
|
|
return any(ip in block for block in ReservedIPBlocks.PRIVATE_IP_SEGMENTS)
|
|
|
|
|
|
@staticmethod
|
|
|
- def get_private_segment(ip):
|
|
|
+ def get_private_segment(ip: IPAddress) -> Optional[IPAddressBlock]:
|
|
|
if not ReservedIPBlocks.is_private(ip):
|
|
|
raise ValueError("%s is not part of a private IP segment" % ip)
|
|
|
|
|
@@ -252,18 +253,18 @@ class ReservedIPBlocks:
|
|
|
return block
|
|
|
|
|
|
@staticmethod
|
|
|
- def is_localhost(ip):
|
|
|
+ def is_localhost(ip: IPAddress) -> bool:
|
|
|
return ip in ReservedIPBlocks.LOCALHOST_SEGMENT
|
|
|
|
|
|
@staticmethod
|
|
|
- def is_multicast(ip):
|
|
|
+ def is_multicast(ip: IPAddressBlock) -> bool:
|
|
|
return ip in ReservedIPBlocks.MULTICAST_SEGMENT
|
|
|
|
|
|
@staticmethod
|
|
|
- def is_reserved(ip):
|
|
|
+ def is_reserved(ip: IPAddress) -> bool:
|
|
|
return ip in ReservedIPBlocks.RESERVED_SEGMENT
|
|
|
|
|
|
@staticmethod
|
|
|
- def is_zero_conf(ip):
|
|
|
+ def is_zero_conf(ip: IPAddressBlock) -> bool:
|
|
|
return ip in ReservedIPBlocks.ZERO_CONF_SEGMENT
|
|
|
|