From a251870ff971db41beb72755202d26520799d255 Mon Sep 17 00:00:00 2001 From: Lari Liuhamo Date: Fri, 10 Jan 2025 16:51:20 +0200 Subject: [PATCH] [MINOR] Linter fixes --- iplib3/__init__.py | 4 +- iplib3/address.py | 193 +++++++-------- iplib3/constants/__init__.py | 2 +- iplib3/constants/address.py | 4 +- iplib3/constants/ipv4.py | 2 +- iplib3/constants/ipv6.py | 2 +- iplib3/constants/port.py | 2 +- iplib3/constants/subnet.py | 11 +- iplib3/subnet.py | 96 ++++---- iplib3/validators.py | 116 ++++----- poetry.lock | 421 ++++++++++++++++++--------------- pyproject.toml | 157 +++++------- tests/__init__.py | 1 + tests/test_address.py | 67 +++--- tests/test_cases_address.py | 112 ++++----- tests/test_cases_subnet.py | 28 ++- tests/test_cases_validators.py | 110 ++++----- tests/test_subnet.py | 35 +-- tests/test_validators.py | 26 +- 19 files changed, 679 insertions(+), 710 deletions(-) diff --git a/iplib3/__init__.py b/iplib3/__init__.py index 6977448..ff20b7a 100644 --- a/iplib3/__init__.py +++ b/iplib3/__init__.py @@ -1,7 +1,7 @@ -"""A pathlib-equivalent library for IP addresses""" +"""A pathlib-equivalent library for IP addresses.""" from iplib3.address import * from iplib3.subnet import * from iplib3.validators import * -__all__ = ('IPAddress', 'IPv4', 'IPv6', 'port_validator') +__all__ = ("IPAddress", "IPv4", "IPv6", "port_validator") diff --git a/iplib3/address.py b/iplib3/address.py index 7723f09..c9a6105 100644 --- a/iplib3/address.py +++ b/iplib3/address.py @@ -1,9 +1,9 @@ -"""iplib3's functionality specific to addresses""" +"""iplib3's functionality specific to addresses.""" from __future__ import annotations from itertools import groupby -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, overload from iplib3.constants.address import ( IPV4_LOCALHOST, @@ -32,20 +32,19 @@ if TYPE_CHECKING: from iplib3.subnet import SubnetMask -__all__ = ('IPAddress', 'IPv4', 'IPv6') +__all__ = ("IPAddress", "IPv4", "IPv6") class PureAddress: - """Bare-bones, independent base class for IP addresses""" + """Bare-bones, independent base class for IP addresses.""" - __slots__ = ('_num', '_port') + __slots__ = ("_num", "_port") def __init__(self: PureAddress, num: int | None = None, port: int | None = None) -> None: self._num: int = num if num is not None else 0 self._port: int | None = port if port_validator(port) else None def __eq__(self: PureAddress, other: object) -> bool: - if isinstance(other, PureAddress): return self.num == other.num and self.port == other.port @@ -63,22 +62,19 @@ def __str__(self: PureAddress) -> str: @property def num(self: PureAddress) -> int: """ - Negative numbers aren't valid, - they are treated as zero. + Negative numbers aren't valid, they are treated as zero. TODO: Consider whether negative numbers should raise an exception """ - return max(0, self._num) @property def port(self: PureAddress) -> int | None: """ - Returns the port in the address, or None if no port is specified + Returns the port in the address, or None if no port is specified. TODO: Consider whether invalid port numbers should raise an exception """ - if self._port is None: return None @@ -87,101 +83,91 @@ def port(self: PureAddress) -> int | None: @port.setter def port(self: PureAddress, value: int | None) -> None: """ - Sets a new port value. Value must be a valid integer and within the range of valid ports. + Set a new port value. Value must be a valid integer and within the range of valid ports. TODO: Find a way to avoid mutability """ - if value is None: pass # OK elif not isinstance(value, int): - raise TypeError(f"Port '{value}' is not a valid integer") + msg = f"Port '{value}' is not a valid integer" + raise TypeError(msg) elif not PORT_NUMBER_MIN_VALUE <= value <= PORT_NUMBER_MAX_VALUE: - raise ValueError( - f"Port number '{value}' not in valid range " - f"({PORT_NUMBER_MIN_VALUE}-{PORT_NUMBER_MAX_VALUE})", - ) + msg = f"Port number '{value}' not in valid range ({PORT_NUMBER_MIN_VALUE}-{PORT_NUMBER_MAX_VALUE})" + raise ValueError(msg) self._port = value @property def as_hex(self: PureAddress) -> str: - """ - Returns a hexadecimal representation of the address - """ - + """Return a hexadecimal representation of the address.""" return f"0x{self.num:0X}" def num_to_ipv4(self: PureAddress) -> str: - """ - A wrapper method for the otherwise equivalent static method - """ - + """Generate an IPv4 string from an integer.""" return self._num_to_ipv4(self.num) - def num_to_ipv6(self: PureAddress, shorten: bool = True, remove_zeroes: bool = False) -> str: - """ - A wrapper method for the otherwise equivalent static method - """ - - return self._num_to_ipv6(self.num, shorten, remove_zeroes) + def num_to_ipv6(self: PureAddress, *, shorten: bool = True, remove_zeroes: bool = False) -> str: + """Generate an IPv6 string from an integer.""" + return self._num_to_ipv6(self.num, shorten=shorten, remove_zeroes=remove_zeroes) @staticmethod def _num_to_ipv4(num: int) -> str: - """ - Generates an IPv4 string from an integer - """ - + """Generate an IPv4 string from an integer.""" segments = [] for _ in range(IPV4_MAX_SEGMENT_COUNT): - num, segment = divmod(num, IPV4_MAX_SEGMENT_VALUE+1) + num, segment = divmod(num, IPV4_MAX_SEGMENT_VALUE + 1) segments.append(segment) - return '.'.join(str(segment) for segment in segments[::-1]) + return ".".join(str(segment) for segment in segments[::-1]) @staticmethod - def _num_to_ipv6(num: int, shorten: bool = True, remove_zeroes: bool = False) -> str: - """ - Generates an IPv6 string from an integer, - with optional zero removal and shortening. - """ - + def _num_to_ipv6(num: int, *, shorten: bool = True, remove_zeroes: bool = False) -> str: + """Generate an IPv6 string from an integer, with optional zero removal and shortening.""" segment_min_length = (IPV6_SEGMENT_BIT_COUNT // IPV6_NUMBER_BIT_COUNT) * (not shorten) - zero_segment = f'{0:0{segment_min_length}}' + zero_segment = f"{0:0{segment_min_length}}" segments = [] for _ in range(IPV6_MAX_SEGMENT_COUNT): - num, segment = divmod(num, IPV6_MAX_SEGMENT_VALUE+1) - segments.append(f'{segment:0{segment_min_length}X}') + num, segment = divmod(num, IPV6_MAX_SEGMENT_VALUE + 1) + segments.append(f"{segment:0{segment_min_length}X}") if remove_zeroes and zero_segment in segments: - # Goes over the segments to find the # longest strip with nothing but zeroes # and replaces it with an empty string. # The final str.join will turn to '::'. longest_idx, length = max( - (idx, len(list(group))) - for idx, (item, group) in enumerate(groupby(segments)) - if item == zero_segment + (idx, len(list(group))) for idx, (item, group) in enumerate(groupby(segments)) if item == zero_segment ) - segments[longest_idx:longest_idx+length] = ['', ''] + segments[longest_idx : longest_idx + length] = ["", ""] - return ':'.join(segments[::-1]) + return ":".join(segments[::-1]) class IPAddress(PureAddress): - """More flexible PureAddress subclass""" - - __slots__ = ('_ipv4', '_ipv6', '_submask') - - def __new__(cls: type[IPAddress], address: int | str | None = None, port_num: int | None = None, **kwargs) -> IPAddress: # noqa: ANN003,PYI034 - + """More flexible PureAddress subclass.""" + + __slots__ = ("_ipv4", "_ipv6", "_submask") + + @overload + def __new__(cls, address: int | None, port_num: int | None, **kwargs) -> IPAddress: # noqa: ANN003 + ... + @overload + def __new__(cls, address: str, port_num: int | None, **kwargs) -> IPv4 | IPv6: # type: ignore[misc] # noqa: ANN003 + ... + + def __new__( # noqa: D102,PYI034 + cls, + address: int | str | None = None, + port_num: int | None = None, + **kwargs, + ) -> IPAddress: if isinstance(address, str): # Only IPv4-addresses have '.', ':' is used in both IPv4 and IPv6 - cls = IPv4 if '.' in address else IPv6 + cls = IPv4 if "." in address else IPv6 # noqa: PLW0642 self = object.__new__(cls) @@ -189,13 +175,14 @@ def __new__(cls: type[IPAddress], address: int | str | None = None, port_num: in return self def __init__(self: IPAddress, address: int | None = IPV4_LOCALHOST, port_num: int | None = None) -> None: + """Create new IPAddress.""" super().__init__(num=address, port=port_num) self._ipv4: IPv4 | None = None self._ipv6: IPv6 | None = None self._submask: SubnetMask | None = None def __eq__(self: IPAddress, other: object) -> bool: - + """Compare address.""" # To accommodate strings if str(self) == str(other): return True @@ -203,7 +190,7 @@ def __eq__(self: IPAddress, other: object) -> bool: return super().__eq__(other) def __str__(self: IPAddress) -> str: - + """Convert to string.""" if IPV4_MIN_VALUE <= self.num <= IPV4_MAX_VALUE: if self._ipv4 is None: self._ipv4 = self.as_ipv4 @@ -214,31 +201,30 @@ def __str__(self: IPAddress) -> str: self._ipv6 = self.as_ipv6 return str(self._ipv6) - raise ValueError(f"No valid address representation exists for {self.num}") + msg = f"No valid address representation exists for {self.num}" + raise ValueError(msg) @property def as_ipv4(self: IPAddress) -> IPv4: - """Creates and returns an IPv4 version of the address, if possible""" - + """Create and return an IPv4 version of the address, if possible.""" return IPv4(self.num_to_ipv4(), port_num=self.port) # type: ignore # noqa: PGH003 @property def as_ipv6(self: IPAddress) -> IPv6: - """Creates and returns an IPv6-version of the address""" - + """Create and return an IPv6-version of the address.""" return IPv6(self.num_to_ipv6(), port_num=self.port) # type: ignore # noqa: PGH003 class IPv4(IPAddress): - """An IPAddress subclass specific to IPv4""" + """An IPAddress subclass specific to IPv4.""" - __slots__ = ('_address',) + __slots__ = ("_address",) def __init__(self: IPv4, address: str | None = None, port_num: int | None = None) -> None: - + """Create new IPv4 address.""" new_address = self._num_to_ipv4(IPV4_LOCALHOST) if address is None else address - _address, *_port = new_address.split(':') + _address, *_port = new_address.split(":") if _port: new_address = _address @@ -250,7 +236,7 @@ def __init__(self: IPv4, address: str | None = None, port_num: int | None = None super().__init__(address=self._ipv4_to_num(), port_num=port_num) def __str__(self: IPv4) -> str: - + """Convert to string.""" if self.port is not None: return f"{self._address}:{self.port}" @@ -258,34 +244,31 @@ def __str__(self: IPv4) -> str: def _ipv4_to_num(self: IPv4) -> int: """ - Takes a valid IPv4 address and turns - it into an equivalent integer value. + Take a valid IPv4 address and turn it into an equivalent integer value. Raises ValueError on invalid IPv4 format. """ - - segments = [int(segment) for segment in self._address.split('.')][::-1] + segments = [int(segment) for segment in self._address.split(".")][::-1] total = 0 for idx, num in enumerate(segments): - total += num * 2**(idx * 8) + total += num * 2 ** (idx * 8) return total class IPv6(IPAddress): - """An IPAddress subclass specific to IPv6""" + """An IPAddress subclass specific to IPv6.""" - __slots__ = ('_address',) + __slots__ = ("_address",) def __init__(self: IPv6, address: str | None = None, port_num: int | None = None) -> None: - + """Create new IPv6 address.""" new_address = self._num_to_ipv6(IPV6_LOCALHOST) if address is None else address - _address, *_port = new_address.split(']:') + _address, *_port = new_address.split("]:") if _port: - # Removes the opening square bracket new_address = _address[1:] @@ -296,7 +279,7 @@ def __init__(self: IPv6, address: str | None = None, port_num: int | None = None super().__init__(address=self._ipv6_to_num(), port_num=port_num) def __str__(self: IPv6) -> str: - + """Convert to string.""" if self.port is not None: return f"[{self._address}]:{self.port}" @@ -304,69 +287,63 @@ def __str__(self: IPv6) -> str: def _ipv6_to_num(self: IPv6) -> int: """ - Takes a valid IPv6 address and turns - it into an equivalent integer value. + Take a valid IPv6 address and turn it into an equivalent integer value. Raises ValueError on invalid IPv6 format. """ - - halves = self._address.split('::') + halves = self._address.split("::") segments = [] if len(halves) == 2: # noqa: PLR2004 # Address with zero-skip part - left, right = (half.split(':') for half in halves) + left, right = (half.split(":") for half in halves) total_length = len(left) + len(right) if halves[0]: segments.extend(left) else: - segments.append('0000') + segments.append("0000") - segments.extend(['0000' for _ in range(IPV6_MAX_SEGMENT_COUNT - total_length)]) + segments.extend(["0000" for _ in range(IPV6_MAX_SEGMENT_COUNT - total_length)]) if halves[1]: segments.extend(right) else: - segments.append('0000') + segments.append("0000") elif len(halves) == 1: # Full address - segments.extend(halves[0].split(':')) + segments.extend(halves[0].split(":")) else: - raise ValueError("Invalid IPv6 address format; only one zero-skip allowed") + msg = "Invalid IPv6 address format; only one zero-skip allowed" + raise ValueError(msg) try: processed_segments: list[int] = [ - int(segment, IPV6_SEGMENT_BIT_COUNT) if segment else 0 - for segment in segments[::-1] + int(segment, IPV6_SEGMENT_BIT_COUNT) if segment else 0 for segment in segments[::-1] ] except ValueError as err: - raise ValueError("Invalid IPv6 address format; address contains invalid characters") from err + msg = "Invalid IPv6 address format; address contains invalid characters" + raise ValueError(msg) from err segment_count = len(processed_segments) if segment_count > IPV6_MAX_SEGMENT_COUNT: - raise ValueError( - f"Invalid IPv6 address format; too many segments " - f"({segment_count} > {IPV6_MAX_SEGMENT_COUNT})", - ) + msg = f"Invalid IPv6 address format; too many segments ({segment_count} > {IPV6_MAX_SEGMENT_COUNT})" + raise ValueError(msg) highest = max(processed_segments) if highest > IPV6_MAX_SEGMENT_VALUE: - raise ValueError( - f"Invalid IPv6 address format; segment max value " - f"passed ({highest} > {IPV6_MAX_SEGMENT_VALUE})", - ) + msg = f"Invalid IPv6 address format; segment max value passed ({highest} > {IPV6_MAX_SEGMENT_VALUE})" + raise ValueError(msg) lowest = min(processed_segments) if lowest < IPV6_MIN_SEGMENT_VALUE: - raise ValueError( - f"Invalid IPv6 address format; segment min value passed ({lowest} < 0)", - ) + msg = f"Invalid IPv6 address format; segment min value passed ({lowest} < 0)" + raise ValueError(msg) total = 0 for idx, num in enumerate(processed_segments): - total += num * 2**(idx * 16) + total += num * 2 ** (idx * 16) return total diff --git a/iplib3/constants/__init__.py b/iplib3/constants/__init__.py index ee717fc..de3bb34 100644 --- a/iplib3/constants/__init__.py +++ b/iplib3/constants/__init__.py @@ -1,4 +1,4 @@ -"""Various constant values used by iplib3""" +"""Various constant values used by iplib3.""" from iplib3.constants.address import * from iplib3.constants.ipv4 import * diff --git a/iplib3/constants/address.py b/iplib3/constants/address.py index 6fba0d6..acd63ec 100644 --- a/iplib3/constants/address.py +++ b/iplib3/constants/address.py @@ -1,4 +1,4 @@ -"""Special addresses in hex values""" +"""Special addresses in hex values.""" IPV4_LOCALHOST = 0x7F_00_00_01 # 127.0.0.1, or 2130706433 -IPV6_LOCALHOST = 0x1 # ::1 +IPV6_LOCALHOST = 0x1 # ::1 diff --git a/iplib3/constants/ipv4.py b/iplib3/constants/ipv4.py index bf58a54..2e0b8b4 100644 --- a/iplib3/constants/ipv4.py +++ b/iplib3/constants/ipv4.py @@ -1,4 +1,4 @@ -"""IPv4 constants""" +"""IPv4 constants.""" # IPv4 constants IPV4_SEGMENT_BIT_COUNT = 8 diff --git a/iplib3/constants/ipv6.py b/iplib3/constants/ipv6.py index d6218ec..f627385 100644 --- a/iplib3/constants/ipv6.py +++ b/iplib3/constants/ipv6.py @@ -1,4 +1,4 @@ -"""IPv6 constants""" +"""IPv6 constants.""" # IPv6 constants IPV6_NUMBER_BIT_COUNT = 4 diff --git a/iplib3/constants/port.py b/iplib3/constants/port.py index ab43426..2376142 100644 --- a/iplib3/constants/port.py +++ b/iplib3/constants/port.py @@ -1,4 +1,4 @@ -"""Port number constants""" +"""Port number constants.""" # Port number constants (agnostic between IPV4 and IPV6) PORT_NUMBER_MIN_VALUE = 0 diff --git a/iplib3/constants/subnet.py b/iplib3/constants/subnet.py index 7e1ddba..95aa450 100644 --- a/iplib3/constants/subnet.py +++ b/iplib3/constants/subnet.py @@ -1,4 +1,4 @@ -"""Subnet constants""" +"""Subnet constants.""" from __future__ import annotations @@ -16,8 +16,10 @@ class SubnetType(str, Enum): - IPV4 = 'ipv4' - IPV6 = 'ipv6' + """Subnet type.""" + + IPV4 = "ipv4" + IPV6 = "ipv6" @classmethod def _missing_(cls: type[SubnetType], value: object) -> SubnetType: @@ -25,4 +27,5 @@ def _missing_(cls: type[SubnetType], value: object) -> SubnetType: if isinstance(value, str) and member.value == value.lower(): return member - raise ValueError("Invalid subnet type") + msg = "Invalid subnet type" + raise ValueError(msg) diff --git a/iplib3/subnet.py b/iplib3/subnet.py index 936ebaa..6d31274 100644 --- a/iplib3/subnet.py +++ b/iplib3/subnet.py @@ -1,4 +1,4 @@ -"""iplib3's functionality specific to subnetting""" +"""iplib3's functionality specific to subnetting.""" from __future__ import annotations @@ -18,22 +18,24 @@ class PureSubnetMask: - """ - Platform and version-independent base class for subnets - """ - __slots__ = ('_prefix_length',) + """Platform and version-independent base class for subnets.""" + + __slots__ = ("_prefix_length",) def __init__(self: PureSubnetMask) -> None: + """Create new pure subnet mask.""" self._prefix_length: int | None = 0 def __str__(self: PureSubnetMask) -> str: + """Convert to string.""" return str(self.prefix_length) def __repr__(self: PureSubnetMask) -> str: + """Generate debug representation.""" return f"iplib3.{self.__class__.__name__}('{self}')" def __eq__(self: PureSubnetMask, other: object) -> bool: - + """Compare equality.""" # To accommodate strings and integers if self.prefix_length == other or str(self) == other: return True @@ -44,17 +46,16 @@ def __eq__(self: PureSubnetMask, other: object) -> bool: return False def __ne__(self: PureSubnetMask, other: object) -> bool: + """Compare inequality.""" return not self == other @property def prefix_length(self: PureSubnetMask) -> int | None: """ - Negative numbers aren't valid, - they are treated as zero. + Negative numbers aren't valid, they are treated as zero. TODO: Consider whether negative numbers should raise an exception """ - if self._prefix_length is None: return None @@ -62,16 +63,20 @@ def prefix_length(self: PureSubnetMask) -> int | None: class SubnetMask(PureSubnetMask): - """Subnet mask for defining subnets""" - - __slots__ = ('_subnet_type',) + """Subnet mask for defining subnets.""" - def __init__(self: SubnetMask, subnet_mask: int | str | None = None, subnet_type: SubnetType = SubnetType.IPV6) -> None: + __slots__ = ("_subnet_type",) + def __init__( + self: SubnetMask, + subnet_mask: int | str | None = None, + subnet_type: SubnetType = SubnetType.IPV6, + ) -> None: + """Create new subnet mask.""" subnet_type = SubnetType(subnet_type) super().__init__() - if isinstance(subnet_mask, str) and '.' in subnet_mask: + if isinstance(subnet_mask, str) and "." in subnet_mask: # Only subnets for IPv4 use strings subnet_type = SubnetType.IPV4 @@ -79,6 +84,7 @@ def __init__(self: SubnetMask, subnet_mask: int | str | None = None, subnet_type self._subnet_type = subnet_type def __repr__(self: SubnetMask) -> str: + """Generate debug representation.""" if self._subnet_type == SubnetType.IPV4 and self._prefix_length is not None: return ( f"iplib3.{self.__class__.__name__}" @@ -88,91 +94,83 @@ def __repr__(self: SubnetMask) -> str: @overload @staticmethod - def _subnet_to_num(subnet_mask: None, subnet_type: SubnetType) -> None: - ... + def _subnet_to_num(subnet_mask: None, subnet_type: SubnetType) -> None: ... @overload @staticmethod - def _subnet_to_num(subnet_mask: int | str, subnet_type: SubnetType) -> int: - ... + def _subnet_to_num(subnet_mask: int | str, subnet_type: SubnetType) -> int: ... @staticmethod def _subnet_to_num(subnet_mask: int | str | None, subnet_type: SubnetType = SubnetType.IPV6) -> int | None: - subnet_type = SubnetType(subnet_type) if subnet_mask is None: return None if not isinstance(subnet_mask, (int, str)): - raise TypeError( - f"Invalid type for subnet value: '{subnet_mask.__class__.__name__}'\n" - f"Expected int, string, or None", - ) + msg = f"Invalid type for subnet value: '{subnet_mask.__class__.__name__}'\nExpected int, string, or None" + raise TypeError(msg) if subnet_type == SubnetType.IPV4: subnet_mask = SubnetMask._ipv4_subnet_to_num(subnet_mask) if subnet_type == SubnetType.IPV6: if isinstance(subnet_mask, str): - if '.' in subnet_mask: - raise ValueError("IPv6-subnets don't use a string representation") + if "." in subnet_mask: + msg = "IPv6-subnets don't use a string representation" + raise ValueError(msg) subnet_mask = int(subnet_mask) if not IPV6_MIN_SUBNET_VALUE <= subnet_mask <= IPV6_MAX_SUBNET_VALUE: - raise ValueError( - f"Subnet '{subnet_mask}' not in valid range " - f"({IPV6_MIN_SUBNET_VALUE}-{IPV6_MAX_SUBNET_VALUE})", - ) + msg = f"Subnet '{subnet_mask}' not in valid range ({IPV6_MIN_SUBNET_VALUE}-{IPV6_MAX_SUBNET_VALUE})" + raise ValueError(msg) return int(subnet_mask) @staticmethod def _ipv4_subnet_to_num(subnet_mask: int | str) -> int: if isinstance(subnet_mask, str): - if '.' in subnet_mask: - segments = tuple(int(s) for s in reversed(subnet_mask.split('.'))) + if "." in subnet_mask: + segments = tuple(int(s) for s in reversed(subnet_mask.split("."))) if len(segments) != IPV4_MIN_SEGMENT_COUNT: - raise ValueError( - f"Subnet value not valid; '{subnet_mask}' is not a valid string representation", - ) + msg = f"Subnet value not valid; '{subnet_mask}' is not a valid string representation" + raise ValueError(msg) - segment_sum = sum(s<<(8*idx) for idx, s in enumerate(segments)) - subnet_bits = f'{segment_sum:b}'.rstrip('0') + segment_sum = sum(s << (8 * idx) for idx, s in enumerate(segments)) + subnet_bits = f"{segment_sum:b}".rstrip("0") - if '0' in subnet_bits: - raise ValueError(f"'{subnet_mask}' is an invalid subnet mask") + if "0" in subnet_bits: + msg = f"'{subnet_mask}' is an invalid subnet mask" + raise ValueError(msg) subnet_mask = len(subnet_bits) try: subnet_mask = int(subnet_mask) except ValueError as err: - raise ValueError( - f"Subnet value not valid; '{subnet_mask}' is neither a valid string representation nor an integer", - ) from err + msg = f"Subnet value not valid; '{subnet_mask}' is neither a valid string representation nor an integer" + raise ValueError(msg) from err if not IPV4_MIN_SUBNET_VALUE <= subnet_mask <= IPV4_MAX_SUBNET_VALUE: - raise ValueError( - f"Subnet '{subnet_mask}' not in valid range " - f"({IPV4_MIN_SUBNET_VALUE}-{IPV4_MAX_SUBNET_VALUE})", - ) + msg = f"Subnet '{subnet_mask}' not in valid range ({IPV4_MIN_SUBNET_VALUE}-{IPV4_MAX_SUBNET_VALUE})" + raise ValueError(msg) return subnet_mask @staticmethod def _prefix_to_subnet_mask(prefix_length: int, subnet_type: SubnetType) -> str: - subnet_type = SubnetType(subnet_type) if subnet_type == SubnetType.IPV6: - raise ValueError("IPv6 does not support string representations of subnet masks") + msg = "IPv6 does not support string representations of subnet masks" + raise ValueError(msg) if not IPV4_MIN_SUBNET_VALUE <= prefix_length <= IPV4_MAX_SUBNET_VALUE: - raise ValueError(f"Invalid subnet value for IPv4: '{prefix_length}'") + msg = f"Invalid subnet value for IPv4: '{prefix_length}'" + raise ValueError(msg) segments = [0, 0, 0, 0] for idx, _ in enumerate(segments): segments[idx] = 2 ** max(min(IPV4_SEGMENT_BIT_COUNT, prefix_length), 0) - 1 prefix_length -= IPV4_SEGMENT_BIT_COUNT - return '.'.join(map(str, segments)) + return ".".join(map(str, segments)) diff --git a/iplib3/validators.py b/iplib3/validators.py index f349cb9..9ee1e98 100644 --- a/iplib3/validators.py +++ b/iplib3/validators.py @@ -1,4 +1,4 @@ -"""iplib3's functionality specific to validating things""" +"""iplib3's functionality specific to validating things.""" from __future__ import annotations @@ -31,12 +31,12 @@ SubnetType, ) -__all__ = ('port_validator', 'ip_validator', 'ipv4_validator', 'ipv6_validator', 'subnet_validator') +__all__ = ("ip_validator", "ipv4_validator", "ipv6_validator", "port_validator", "subnet_validator") def port_validator(port_num: int | None) -> bool: """ - Validates an address port + Validate an address port. None means "no port", and is treated as a valid port value. Otherwise the port must be an integer between the minimum and maximum port values, inclusive. @@ -45,41 +45,34 @@ def port_validator(port_num: int | None) -> bool: The function should not raise an exception, wrong types will simply return False. """ - if port_num is None: return True - return ( - isinstance(port_num, int) - and PORT_NUMBER_MIN_VALUE <= port_num <= PORT_NUMBER_MAX_VALUE - ) + return isinstance(port_num, int) and PORT_NUMBER_MIN_VALUE <= port_num <= PORT_NUMBER_MAX_VALUE -def ip_validator(address: str | int, strict: bool = True) -> bool: +def ip_validator(address: str | int, *, strict: bool = True) -> bool: """ - Validates an IP address of any kind, returning a boolean + Validate an IP address of any kind, returning a boolean. Under strict mode ensures that the numerical values don't exceed legal bounds, otherwise focuses on form. """ - - if ipv4_validator(address, strict): + if ipv4_validator(address, strict=strict): return True - return ipv6_validator(address, strict) + return ipv6_validator(address, strict=strict) -def ipv4_validator(address: str | int, strict: bool = True) -> bool: +def ipv4_validator(address: str | int, *, strict: bool = True) -> bool: """ - Validates an IPv4 address, returning a boolean + Validate an IPv4 address, returning a boolean. Under strict mode ensures that the numerical values don't exceed legal bounds, otherwise focuses on form. """ - valid = False - if isinstance(address, str) and '.' in address: - + if isinstance(address, str) and "." in address: portless_address, _, valid = _port_stripper(address, protocol=SubnetType.IPV4, strict=strict) if valid: @@ -91,18 +84,16 @@ def ipv4_validator(address: str | int, strict: bool = True) -> bool: return valid -def ipv6_validator(address: str | int, strict: bool = True) -> bool: +def ipv6_validator(address: str | int, *, strict: bool = True) -> bool: """ - Validates an IPv6 address, returning a boolean + Validate an IPv6 address, returning a boolean. Under strict mode ensures that the numerical values don't exceed legal bounds, otherwise focuses on form. """ - valid = False if isinstance(address, str): - portless_address, _, valid = _port_stripper(address, protocol=SubnetType.IPV6, strict=strict) if not valid: @@ -118,12 +109,11 @@ def ipv6_validator(address: str | int, strict: bool = True) -> bool: def subnet_validator(subnet: str | int, protocol: SubnetType = SubnetType.IPV4) -> bool: """ - Validates a given subnet mask, defaulting to IPv4 protocol + Validate a given subnet mask, defaulting to IPv4 protocol. Strings will be validated as IPv4 regardless of the protocol toggle as IPv6 subnets have no valid string representation. """ - protocol = SubnetType(protocol) valid = False @@ -139,7 +129,7 @@ def subnet_validator(subnet: str | int, protocol: SubnetType = SubnetType.IPV4) def _ipv4_subnet_validator(subnet: str | int) -> bool: """ - Validates an IPv4-compliant subnet mask + Validate an IPv4-compliant subnet mask. The function uses the IPv4-standard to validate a subnet, including all values. @@ -148,16 +138,15 @@ def _ipv4_subnet_validator(subnet: str | int) -> bool: *will raise a TypeError* with the name of the used type. """ - if isinstance(subnet, str): - segments = tuple(int(s) for s in reversed(subnet.split('.'))) + segments = tuple(int(s) for s in reversed(subnet.split("."))) if len(segments) != IPV4_MIN_SEGMENT_COUNT: return False - segment_sum = sum(s<<(8*idx) for idx, s in enumerate(segments)) - subnet_bits = f'{segment_sum:b}'.rstrip('0') + segment_sum = sum(s << (8 * idx) for idx, s in enumerate(segments)) + subnet_bits = f"{segment_sum:b}".rstrip("0") - if '0' in subnet_bits: + if "0" in subnet_bits: return False subnet = len(subnet_bits) @@ -165,15 +154,13 @@ def _ipv4_subnet_validator(subnet: str | int) -> bool: if isinstance(subnet, int): return IPV4_MIN_SUBNET_VALUE <= subnet <= IPV4_MAX_SUBNET_VALUE - raise TypeError( - f"IPv4 subnet cannot be of type '{subnet.__class__.__name__}';" - f" only strings and integers supported", - ) + msg = f"IPv4 subnet cannot be of type '{subnet.__class__.__name__}'; only strings and integers supported" + raise TypeError(msg) def _ipv6_subnet_validator(subnet: int) -> bool: # IPv6 subnets have no string representation """ - Validates an IPv6-compliant subnet mask + Validate an IPv6-compliant subnet mask. The IPv6-standard has no string representation for subnests, so @@ -182,24 +169,25 @@ def _ipv6_subnet_validator(subnet: int) -> bool: # IPv6 subnets have no string Non-integer types will raise a ValueError with the name of the used type. """ - if isinstance(subnet, int): - return ( - IPV6_MIN_SUBNET_VALUE <= subnet <= IPV6_MAX_SUBNET_VALUE - and subnet % IPV6_NUMBER_BIT_COUNT == 0 - ) + return IPV6_MIN_SUBNET_VALUE <= subnet <= IPV6_MAX_SUBNET_VALUE and subnet % IPV6_NUMBER_BIT_COUNT == 0 - raise TypeError(f"IPv6 subnet cannot be of type '{subnet.__class__.__name__}', it must be an integer") + msg = f"IPv6 subnet cannot be of type '{subnet.__class__.__name__}', it must be an integer" + raise TypeError(msg) -def _port_stripper(address: str, protocol: SubnetType = SubnetType.IPV4, strict: bool = True) -> tuple[str, int | None, bool]: +def _port_stripper( + address: str, + protocol: SubnetType = SubnetType.IPV4, + *, + strict: bool = True, +) -> tuple[str, int | None, bool]: """ - Extracts the port number from IP addresses, if any + Extract the port number from IP addresses, if any. Returns a tuple with the portless address, the port (or None), and validation information as a boolean. """ - protocol = SubnetType(protocol) valid = True @@ -207,14 +195,13 @@ def _port_stripper(address: str, protocol: SubnetType = SubnetType.IPV4, strict: port_separator = None if protocol == SubnetType.IPV4: - port_separator = ':' + port_separator = ":" if protocol == SubnetType.IPV6: - port_separator = ']:' + port_separator = "]:" # Try split on closing bracket and port separator address, *port = address.strip().split(port_separator) if port and valid: - if protocol == SubnetType.IPV6: # Get rid of the opening bracket that contained the address (eg. [::12:34]:8080 -> ::12:34) address = address[1:] @@ -231,13 +218,12 @@ def _port_stripper(address: str, protocol: SubnetType = SubnetType.IPV4, strict: return address, port_num, valid -def _ipv4_address_validator(address: str, strict: bool = True) -> bool: - """Validates the address part of an IPv4 address""" - +def _ipv4_address_validator(address: str, *, strict: bool = True) -> bool: + """Validate the address part of an IPv4 address.""" valid = True try: - segments = [int(segment) for segment in address.split('.')] + segments = [int(segment) for segment in address.split(".")] except ValueError: # IPv4 address was not made of valid integers return False @@ -256,14 +242,13 @@ def _ipv4_address_validator(address: str, strict: bool = True) -> bool: return valid -def _ipv6_address_validator(address: str, strict: bool = True) -> bool: - """Validates the address part of an IPv6 address""" - +def _ipv6_address_validator(address: str, *, strict: bool = True) -> bool: + """Validate the address part of an IPv6 address.""" address = address.strip() valid = True segments: list[str] = [] - empty_segment = f'{IPV6_MIN_SEGMENT_VALUE:0{IPV6_NUMBER_BIT_COUNT}}' - skips = address.count('::') + empty_segment = f"{IPV6_MIN_SEGMENT_VALUE:0{IPV6_NUMBER_BIT_COUNT}}" + skips = address.count("::") if skips > 1: # More than one, illegal zero-skip @@ -271,10 +256,10 @@ def _ipv6_address_validator(address: str, strict: bool = True) -> bool: elif skips == 1: # One, legal zero-skip - left, *_, right = address.split('::') + left, *_, right = address.split("::") - left_segments = left.split(':') - right_segments = right.split(':') + left_segments = left.split(":") + right_segments = right.split(":") total_segments = len(left_segments) + len(right_segments) segments.extend(left_segments if left else [empty_segment]) @@ -283,14 +268,10 @@ def _ipv6_address_validator(address: str, strict: bool = True) -> bool: else: # No zero-skip, full address - segments = address.split(':') - + segments = address.split(":") try: - processed_segments: list[int] = [ - int(segment, IPV6_SEGMENT_BIT_COUNT) if segment else 0 - for segment in segments - ] + processed_segments: list[int] = [int(segment, IPV6_SEGMENT_BIT_COUNT) if segment else 0 for segment in segments] except ValueError: # IPv6 address was not made of valid hexadecimal numbers return False @@ -299,9 +280,6 @@ def _ipv6_address_validator(address: str, strict: bool = True) -> bool: valid = False if strict and valid: - valid = all( - IPV6_MIN_SEGMENT_VALUE <= seg <= IPV6_MAX_SEGMENT_VALUE - for seg in processed_segments - ) + valid = all(IPV6_MIN_SEGMENT_VALUE <= seg <= IPV6_MAX_SEGMENT_VALUE for seg in processed_segments) return valid diff --git a/poetry.lock b/poetry.lock index b1ce9be..491728b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,22 +1,20 @@ -# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "cachetools" -version = "5.3.1" +version = "5.5.0" description = "Extensible memoizing collections and decorators" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, - {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, + {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, + {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, ] [[package]] name = "chardet" version = "5.2.0" description = "Universal encoding detector for Python 3" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -28,7 +26,6 @@ files = [ name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -38,64 +35,73 @@ files = [ [[package]] name = "coverage" -version = "7.3.1" +version = "7.6.10" description = "Code coverage measurement for Python" -category = "dev" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "coverage-7.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cd0f7429ecfd1ff597389907045ff209c8fdb5b013d38cfa7c60728cb484b6e3"}, - {file = "coverage-7.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:966f10df9b2b2115da87f50f6a248e313c72a668248be1b9060ce935c871f276"}, - {file = "coverage-7.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0575c37e207bb9b98b6cf72fdaaa18ac909fb3d153083400c2d48e2e6d28bd8e"}, - {file = "coverage-7.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:245c5a99254e83875c7fed8b8b2536f040997a9b76ac4c1da5bff398c06e860f"}, - {file = "coverage-7.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c96dd7798d83b960afc6c1feb9e5af537fc4908852ef025600374ff1a017392"}, - {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:de30c1aa80f30af0f6b2058a91505ea6e36d6535d437520067f525f7df123887"}, - {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:50dd1e2dd13dbbd856ffef69196781edff26c800a74f070d3b3e3389cab2600d"}, - {file = "coverage-7.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9c0c19f70d30219113b18fe07e372b244fb2a773d4afde29d5a2f7930765136"}, - {file = "coverage-7.3.1-cp310-cp310-win32.whl", hash = "sha256:770f143980cc16eb601ccfd571846e89a5fe4c03b4193f2e485268f224ab602f"}, - {file = "coverage-7.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:cdd088c00c39a27cfa5329349cc763a48761fdc785879220d54eb785c8a38520"}, - {file = "coverage-7.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:74bb470399dc1989b535cb41f5ca7ab2af561e40def22d7e188e0a445e7639e3"}, - {file = "coverage-7.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:025ded371f1ca280c035d91b43252adbb04d2aea4c7105252d3cbc227f03b375"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6191b3a6ad3e09b6cfd75b45c6aeeffe7e3b0ad46b268345d159b8df8d835f9"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb0b188f30e41ddd659a529e385470aa6782f3b412f860ce22b2491c89b8593"}, - {file = "coverage-7.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c8f0df9dfd8ff745bccff75867d63ef336e57cc22b2908ee725cc552689ec8"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7eb3cd48d54b9bd0e73026dedce44773214064be93611deab0b6a43158c3d5a0"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ac3c5b7e75acac31e490b7851595212ed951889918d398b7afa12736c85e13ce"}, - {file = "coverage-7.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b4ee7080878077af0afa7238df1b967f00dc10763f6e1b66f5cced4abebb0a3"}, - {file = "coverage-7.3.1-cp311-cp311-win32.whl", hash = "sha256:229c0dd2ccf956bf5aeede7e3131ca48b65beacde2029f0361b54bf93d36f45a"}, - {file = "coverage-7.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:c6f55d38818ca9596dc9019eae19a47410d5322408140d9a0076001a3dcb938c"}, - {file = "coverage-7.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5289490dd1c3bb86de4730a92261ae66ea8d44b79ed3cc26464f4c2cde581fbc"}, - {file = "coverage-7.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca833941ec701fda15414be400c3259479bfde7ae6d806b69e63b3dc423b1832"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd694e19c031733e446c8024dedd12a00cda87e1c10bd7b8539a87963685e969"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aab8e9464c00da5cb9c536150b7fbcd8850d376d1151741dd0d16dfe1ba4fd26"}, - {file = "coverage-7.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d38444efffd5b056fcc026c1e8d862191881143c3aa80bb11fcf9dca9ae204"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8a07b692129b8a14ad7a37941a3029c291254feb7a4237f245cfae2de78de037"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2829c65c8faaf55b868ed7af3c7477b76b1c6ebeee99a28f59a2cb5907a45760"}, - {file = "coverage-7.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f111a7d85658ea52ffad7084088277135ec5f368457275fc57f11cebb15607f"}, - {file = "coverage-7.3.1-cp312-cp312-win32.whl", hash = "sha256:c397c70cd20f6df7d2a52283857af622d5f23300c4ca8e5bd8c7a543825baa5a"}, - {file = "coverage-7.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:5ae4c6da8b3d123500f9525b50bf0168023313963e0e2e814badf9000dd6ef92"}, - {file = "coverage-7.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ca70466ca3a17460e8fc9cea7123c8cbef5ada4be3140a1ef8f7b63f2f37108f"}, - {file = "coverage-7.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f2781fd3cabc28278dc982a352f50c81c09a1a500cc2086dc4249853ea96b981"}, - {file = "coverage-7.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6407424621f40205bbe6325686417e5e552f6b2dba3535dd1f90afc88a61d465"}, - {file = "coverage-7.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04312b036580ec505f2b77cbbdfb15137d5efdfade09156961f5277149f5e344"}, - {file = "coverage-7.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac9ad38204887349853d7c313f53a7b1c210ce138c73859e925bc4e5d8fc18e7"}, - {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:53669b79f3d599da95a0afbef039ac0fadbb236532feb042c534fbb81b1a4e40"}, - {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:614f1f98b84eb256e4f35e726bfe5ca82349f8dfa576faabf8a49ca09e630086"}, - {file = "coverage-7.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f1a317fdf5c122ad642db8a97964733ab7c3cf6009e1a8ae8821089993f175ff"}, - {file = "coverage-7.3.1-cp38-cp38-win32.whl", hash = "sha256:defbbb51121189722420a208957e26e49809feafca6afeef325df66c39c4fdb3"}, - {file = "coverage-7.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:f4f456590eefb6e1b3c9ea6328c1e9fa0f1006e7481179d749b3376fc793478e"}, - {file = "coverage-7.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f12d8b11a54f32688b165fd1a788c408f927b0960984b899be7e4c190ae758f1"}, - {file = "coverage-7.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f09195dda68d94a53123883de75bb97b0e35f5f6f9f3aa5bf6e496da718f0cb6"}, - {file = "coverage-7.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6601a60318f9c3945be6ea0f2a80571f4299b6801716f8a6e4846892737ebe4"}, - {file = "coverage-7.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07d156269718670d00a3b06db2288b48527fc5f36859425ff7cec07c6b367745"}, - {file = "coverage-7.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:636a8ac0b044cfeccae76a36f3b18264edcc810a76a49884b96dd744613ec0b7"}, - {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5d991e13ad2ed3aced177f524e4d670f304c8233edad3210e02c465351f785a0"}, - {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:586649ada7cf139445da386ab6f8ef00e6172f11a939fc3b2b7e7c9082052fa0"}, - {file = "coverage-7.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4aba512a15a3e1e4fdbfed2f5392ec221434a614cc68100ca99dcad7af29f3f8"}, - {file = "coverage-7.3.1-cp39-cp39-win32.whl", hash = "sha256:6bc6f3f4692d806831c136c5acad5ccedd0262aa44c087c46b7101c77e139140"}, - {file = "coverage-7.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:553d7094cb27db58ea91332e8b5681bac107e7242c23f7629ab1316ee73c4981"}, - {file = "coverage-7.3.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:220eb51f5fb38dfdb7e5d54284ca4d0cd70ddac047d750111a68ab1798945194"}, - {file = "coverage-7.3.1.tar.gz", hash = "sha256:6cb7fe1581deb67b782c153136541e20901aa312ceedaf1467dcb35255787952"}, + {file = "coverage-7.6.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5c912978f7fbf47ef99cec50c4401340436d200d41d714c7a4766f377c5b7b78"}, + {file = "coverage-7.6.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a01ec4af7dfeb96ff0078ad9a48810bb0cc8abcb0115180c6013a6b26237626c"}, + {file = "coverage-7.6.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3b204c11e2b2d883946fe1d97f89403aa1811df28ce0447439178cc7463448a"}, + {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32ee6d8491fcfc82652a37109f69dee9a830e9379166cb73c16d8dc5c2915165"}, + {file = "coverage-7.6.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675cefc4c06e3b4c876b85bfb7c59c5e2218167bbd4da5075cbe3b5790a28988"}, + {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f4f620668dbc6f5e909a0946a877310fb3d57aea8198bde792aae369ee1c23b5"}, + {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4eea95ef275de7abaef630c9b2c002ffbc01918b726a39f5a4353916ec72d2f3"}, + {file = "coverage-7.6.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e2f0280519e42b0a17550072861e0bc8a80a0870de260f9796157d3fca2733c5"}, + {file = "coverage-7.6.10-cp310-cp310-win32.whl", hash = "sha256:bc67deb76bc3717f22e765ab3e07ee9c7a5e26b9019ca19a3b063d9f4b874244"}, + {file = "coverage-7.6.10-cp310-cp310-win_amd64.whl", hash = "sha256:0f460286cb94036455e703c66988851d970fdfd8acc2a1122ab7f4f904e4029e"}, + {file = "coverage-7.6.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ea3c8f04b3e4af80e17bab607c386a830ffc2fb88a5484e1df756478cf70d1d3"}, + {file = "coverage-7.6.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:507a20fc863cae1d5720797761b42d2d87a04b3e5aeb682ef3b7332e90598f43"}, + {file = "coverage-7.6.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37a84878285b903c0fe21ac8794c6dab58150e9359f1aaebbeddd6412d53132"}, + {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a534738b47b0de1995f85f582d983d94031dffb48ab86c95bdf88dc62212142f"}, + {file = "coverage-7.6.10-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d7a2bf79378d8fb8afaa994f91bfd8215134f8631d27eba3e0e2c13546ce994"}, + {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6713ba4b4ebc330f3def51df1d5d38fad60b66720948112f114968feb52d3f99"}, + {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab32947f481f7e8c763fa2c92fd9f44eeb143e7610c4ca9ecd6a36adab4081bd"}, + {file = "coverage-7.6.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7bbd8c8f1b115b892e34ba66a097b915d3871db7ce0e6b9901f462ff3a975377"}, + {file = "coverage-7.6.10-cp311-cp311-win32.whl", hash = "sha256:299e91b274c5c9cdb64cbdf1b3e4a8fe538a7a86acdd08fae52301b28ba297f8"}, + {file = "coverage-7.6.10-cp311-cp311-win_amd64.whl", hash = "sha256:489a01f94aa581dbd961f306e37d75d4ba16104bbfa2b0edb21d29b73be83609"}, + {file = "coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853"}, + {file = "coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078"}, + {file = "coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0"}, + {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50"}, + {file = "coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022"}, + {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b"}, + {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0"}, + {file = "coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852"}, + {file = "coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359"}, + {file = "coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247"}, + {file = "coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9"}, + {file = "coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b"}, + {file = "coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690"}, + {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18"}, + {file = "coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c"}, + {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd"}, + {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e"}, + {file = "coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694"}, + {file = "coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6"}, + {file = "coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e"}, + {file = "coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe"}, + {file = "coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273"}, + {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8"}, + {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098"}, + {file = "coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb"}, + {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0"}, + {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf"}, + {file = "coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2"}, + {file = "coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312"}, + {file = "coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d"}, + {file = "coverage-7.6.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:656c82b8a0ead8bba147de9a89bda95064874c91a3ed43a00e687f23cc19d53a"}, + {file = "coverage-7.6.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccc2b70a7ed475c68ceb548bf69cec1e27305c1c2606a5eb7c3afff56a1b3b27"}, + {file = "coverage-7.6.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5e37dc41d57ceba70956fa2fc5b63c26dba863c946ace9705f8eca99daecdc4"}, + {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0aa9692b4fdd83a4647eeb7db46410ea1322b5ed94cd1715ef09d1d5922ba87f"}, + {file = "coverage-7.6.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa744da1820678b475e4ba3dfd994c321c5b13381d1041fe9c608620e6676e25"}, + {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0b1818063dc9e9d838c09e3a473c1422f517889436dd980f5d721899e66f315"}, + {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:59af35558ba08b758aec4d56182b222976330ef8d2feacbb93964f576a7e7a90"}, + {file = "coverage-7.6.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7ed2f37cfce1ce101e6dffdfd1c99e729dd2ffc291d02d3e2d0af8b53d13840d"}, + {file = "coverage-7.6.10-cp39-cp39-win32.whl", hash = "sha256:4bcc276261505d82f0ad426870c3b12cb177752834a633e737ec5ee79bbdff18"}, + {file = "coverage-7.6.10-cp39-cp39-win_amd64.whl", hash = "sha256:457574f4599d2b00f7f637a0700a6422243b3565509457b2dbd3f50703e11f59"}, + {file = "coverage-7.6.10-pp39.pp310-none-any.whl", hash = "sha256:fd34e7b3405f0cc7ab03d54a334c17a9e802897580d964bd8c2001f4b9fd488f"}, + {file = "coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23"}, ] [package.dependencies] @@ -106,26 +112,24 @@ toml = ["tomli"] [[package]] name = "distlib" -version = "0.3.7" +version = "0.3.9" description = "Distribution utilities" -category = "dev" optional = false python-versions = "*" files = [ - {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, - {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, + {file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"}, + {file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"}, ] [[package]] name = "exceptiongroup" -version = "1.1.3" +version = "1.2.2" description = "Backport of PEP 654 (exception groups)" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, - {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, ] [package.extras] @@ -133,26 +137,24 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.12.4" +version = "3.16.1" description = "A platform independent file lock." -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.12.4-py3-none-any.whl", hash = "sha256:08c21d87ded6e2b9da6728c3dff51baf1dcecf973b768ef35bcbc3447edb9ad4"}, - {file = "filelock-3.12.4.tar.gz", hash = "sha256:2e6f249f1f3654291606e046b09f1fd5eac39b360664c27f5aad072012f8bcbd"}, + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-timeout (>=2.1)"] -typing = ["typing-extensions (>=4.7.1)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] +typing = ["typing-extensions (>=4.12.2)"] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -162,56 +164,67 @@ files = [ [[package]] name = "mypy" -version = "1.5.1" +version = "1.14.1" description = "Optional static typing for Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f33592ddf9655a4894aef22d134de7393e95fcbdc2d15c1ab65828eee5c66c70"}, - {file = "mypy-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:258b22210a4a258ccd077426c7a181d789d1121aca6db73a83f79372f5569ae0"}, - {file = "mypy-1.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9ec1f695f0c25986e6f7f8778e5ce61659063268836a38c951200c57479cc12"}, - {file = "mypy-1.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:abed92d9c8f08643c7d831300b739562b0a6c9fcb028d211134fc9ab20ccad5d"}, - {file = "mypy-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:a156e6390944c265eb56afa67c74c0636f10283429171018446b732f1a05af25"}, - {file = "mypy-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ac9c21bfe7bc9f7f1b6fae441746e6a106e48fc9de530dea29e8cd37a2c0cc4"}, - {file = "mypy-1.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51cb1323064b1099e177098cb939eab2da42fea5d818d40113957ec954fc85f4"}, - {file = "mypy-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:596fae69f2bfcb7305808c75c00f81fe2829b6236eadda536f00610ac5ec2243"}, - {file = "mypy-1.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:32cb59609b0534f0bd67faebb6e022fe534bdb0e2ecab4290d683d248be1b275"}, - {file = "mypy-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:159aa9acb16086b79bbb0016145034a1a05360626046a929f84579ce1666b315"}, - {file = "mypy-1.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f6b0e77db9ff4fda74de7df13f30016a0a663928d669c9f2c057048ba44f09bb"}, - {file = "mypy-1.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:26f71b535dfc158a71264e6dc805a9f8d2e60b67215ca0bfa26e2e1aa4d4d373"}, - {file = "mypy-1.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc3a600f749b1008cc75e02b6fb3d4db8dbcca2d733030fe7a3b3502902f161"}, - {file = "mypy-1.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:26fb32e4d4afa205b24bf645eddfbb36a1e17e995c5c99d6d00edb24b693406a"}, - {file = "mypy-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:82cb6193de9bbb3844bab4c7cf80e6227d5225cc7625b068a06d005d861ad5f1"}, - {file = "mypy-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4a465ea2ca12804d5b34bb056be3a29dc47aea5973b892d0417c6a10a40b2d65"}, - {file = "mypy-1.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9fece120dbb041771a63eb95e4896791386fe287fefb2837258925b8326d6160"}, - {file = "mypy-1.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d28ddc3e3dfeab553e743e532fb95b4e6afad51d4706dd22f28e1e5e664828d2"}, - {file = "mypy-1.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:57b10c56016adce71fba6bc6e9fd45d8083f74361f629390c556738565af8eeb"}, - {file = "mypy-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:ff0cedc84184115202475bbb46dd99f8dcb87fe24d5d0ddfc0fe6b8575c88d2f"}, - {file = "mypy-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8f772942d372c8cbac575be99f9cc9d9fb3bd95c8bc2de6c01411e2c84ebca8a"}, - {file = "mypy-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5d627124700b92b6bbaa99f27cbe615c8ea7b3402960f6372ea7d65faf376c14"}, - {file = "mypy-1.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:361da43c4f5a96173220eb53340ace68cda81845cd88218f8862dfb0adc8cddb"}, - {file = "mypy-1.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:330857f9507c24de5c5724235e66858f8364a0693894342485e543f5b07c8693"}, - {file = "mypy-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:c543214ffdd422623e9fedd0869166c2f16affe4ba37463975043ef7d2ea8770"}, - {file = "mypy-1.5.1-py3-none-any.whl", hash = "sha256:f757063a83970d67c444f6e01d9550a7402322af3557ce7630d3c957386fa8f5"}, - {file = "mypy-1.5.1.tar.gz", hash = "sha256:b031b9601f1060bf1281feab89697324726ba0c0bae9d7cd7ab4b690940f0b92"}, + {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, + {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, + {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"}, + {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"}, + {file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"}, + {file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"}, + {file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"}, + {file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"}, + {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"}, + {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"}, + {file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"}, + {file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"}, + {file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"}, + {file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"}, + {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"}, + {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"}, + {file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"}, + {file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"}, + {file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"}, + {file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"}, + {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"}, + {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"}, + {file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"}, + {file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"}, + {file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"}, + {file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"}, + {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"}, + {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"}, + {file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"}, + {file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"}, + {file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"}, + {file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"}, + {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"}, + {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"}, + {file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"}, + {file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"}, + {file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"}, + {file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"}, ] [package.dependencies] -mypy-extensions = ">=1.0.0" +mypy_extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=4.1.0" +typing_extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] install-types = ["pip"] +mypyc = ["setuptools (>=50)"] reports = ["lxml"] [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -221,42 +234,40 @@ files = [ [[package]] name = "packaging" -version = "23.1" +version = "24.2" description = "Core utilities for Python packages" -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] name = "platformdirs" -version = "3.10.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" +version = "4.3.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, - {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.11.2)"] [[package]] name = "pluggy" -version = "1.3.0" +version = "1.5.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, + {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] @@ -265,34 +276,32 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pyproject-api" -version = "1.6.1" +version = "1.8.0" description = "API to interact with the python pyproject.toml based projects" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "pyproject_api-1.6.1-py3-none-any.whl", hash = "sha256:4c0116d60476b0786c88692cf4e325a9814965e2469c5998b830bba16b183675"}, - {file = "pyproject_api-1.6.1.tar.gz", hash = "sha256:1817dc018adc0d1ff9ca1ed8c60e1623d5aaca40814b953af14a9cf9a5cae538"}, + {file = "pyproject_api-1.8.0-py3-none-any.whl", hash = "sha256:3d7d347a047afe796fd5d1885b1e391ba29be7169bd2f102fcd378f04273d228"}, + {file = "pyproject_api-1.8.0.tar.gz", hash = "sha256:77b8049f2feb5d33eefcc21b57f1e279636277a8ac8ad6b5871037b243778496"}, ] [package.dependencies] -packaging = ">=23.1" +packaging = ">=24.1" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} [package.extras] -docs = ["furo (>=2023.8.19)", "sphinx (<7.2)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "setuptools (>=68.1.2)", "wheel (>=0.41.2)"] +docs = ["furo (>=2024.8.6)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "pytest (>=8.3.3)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "setuptools (>=75.1)"] [[package]] name = "pytest" -version = "7.4.2" +version = "7.4.4" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.2-py3-none-any.whl", hash = "sha256:1d881c6124e08ff0a1bb75ba3ec0bfd8b5354a01c194ddd5a0a870a48d99b002"}, - {file = "pytest-7.4.2.tar.gz", hash = "sha256:a766259cfab564a2ad52cb1aae1b881a75c3eb7e34ca3779697c23ed47c47069"}, + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] @@ -310,7 +319,6 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pytest-cov" version = "4.1.0" description = "Pytest plugin for measuring coverage." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -327,81 +335,108 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "ruff" -version = "0.0.290" -description = "An extremely fast Python linter, written in Rust." -category = "dev" +version = "0.9.0" +description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.0.290-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:0e2b09ac4213b11a3520221083866a5816616f3ae9da123037b8ab275066fbac"}, - {file = "ruff-0.0.290-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:4ca6285aa77b3d966be32c9a3cd531655b3d4a0171e1f9bf26d66d0372186767"}, - {file = "ruff-0.0.290-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35e3550d1d9f2157b0fcc77670f7bb59154f223bff281766e61bdd1dd854e0c5"}, - {file = "ruff-0.0.290-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d748c8bd97874f5751aed73e8dde379ce32d16338123d07c18b25c9a2796574a"}, - {file = "ruff-0.0.290-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:982af5ec67cecd099e2ef5e238650407fb40d56304910102d054c109f390bf3c"}, - {file = "ruff-0.0.290-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:bbd37352cea4ee007c48a44c9bc45a21f7ba70a57edfe46842e346651e2b995a"}, - {file = "ruff-0.0.290-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d9be6351b7889462912e0b8185a260c0219c35dfd920fb490c7f256f1d8313e"}, - {file = "ruff-0.0.290-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75cdc7fe32dcf33b7cec306707552dda54632ac29402775b9e212a3c16aad5e6"}, - {file = "ruff-0.0.290-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb07f37f7aecdbbc91d759c0c09870ce0fb3eed4025eebedf9c4b98c69abd527"}, - {file = "ruff-0.0.290-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2ab41bc0ba359d3f715fc7b705bdeef19c0461351306b70a4e247f836b9350ed"}, - {file = "ruff-0.0.290-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:150bf8050214cea5b990945b66433bf9a5e0cef395c9bc0f50569e7de7540c86"}, - {file = "ruff-0.0.290-py3-none-musllinux_1_2_i686.whl", hash = "sha256:75386ebc15fe5467248c039f5bf6a0cfe7bfc619ffbb8cd62406cd8811815fca"}, - {file = "ruff-0.0.290-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ac93eadf07bc4ab4c48d8bb4e427bf0f58f3a9c578862eb85d99d704669f5da0"}, - {file = "ruff-0.0.290-py3-none-win32.whl", hash = "sha256:461fbd1fb9ca806d4e3d5c745a30e185f7cf3ca77293cdc17abb2f2a990ad3f7"}, - {file = "ruff-0.0.290-py3-none-win_amd64.whl", hash = "sha256:f1f49f5ec967fd5778813780b12a5650ab0ebcb9ddcca28d642c689b36920796"}, - {file = "ruff-0.0.290-py3-none-win_arm64.whl", hash = "sha256:ae5a92dfbdf1f0c689433c223f8dac0782c2b2584bd502dfdbc76475669f1ba1"}, - {file = "ruff-0.0.290.tar.gz", hash = "sha256:949fecbc5467bb11b8db810a7fa53c7e02633856ee6bd1302b2f43adcd71b88d"}, + {file = "ruff-0.9.0-py3-none-linux_armv6l.whl", hash = "sha256:949b3513f931741e006cf267bf89611edff04e1f012013424022add3ce78f319"}, + {file = "ruff-0.9.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:99fbcb8c7fe94ae1e462ab2a1ef17cb20b25fb6438b9f198b1bcf5207a0a7916"}, + {file = "ruff-0.9.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:0b022afd8eb0fcfce1e0adec84322abf4d6ce3cd285b3b99c4f17aae7decf749"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:336567ce92c9ca8ec62780d07b5fa11fbc881dc7bb40958f93a7d621e7ab4589"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d338336c44bda602dc8e8766836ac0441e5b0dfeac3af1bd311a97ebaf087a75"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9b3ececf523d733e90b540e7afcc0494189e8999847f8855747acd5a9a8c45f"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a11c0872a31232e473e2e0e2107f3d294dbadd2f83fb281c3eb1c22a24866924"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5fd06220c17a9cc0dc7fc6552f2ac4db74e8e8bff9c401d160ac59d00566f54"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0457e775c74bf3976243f910805242b7dcd389e1d440deccbd1194ca17a5728c"}, + {file = "ruff-0.9.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05415599bbcb318f730ea1b46a39e4fbf71f6a63fdbfa1dda92efb55f19d7ecf"}, + {file = "ruff-0.9.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:fbf9864b009e43cfc1c8bed1a6a4c529156913105780af4141ca4342148517f5"}, + {file = "ruff-0.9.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:37b3da222b12e2bb2ce628e02586ab4846b1ed7f31f42a5a0683b213453b2d49"}, + {file = "ruff-0.9.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:733c0fcf2eb0c90055100b4ed1af9c9d87305b901a8feb6a0451fa53ed88199d"}, + {file = "ruff-0.9.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8221a454bfe5ccdf8017512fd6bb60e6ec30f9ea252b8a80e5b73619f6c3cefd"}, + {file = "ruff-0.9.0-py3-none-win32.whl", hash = "sha256:d345f2178afd192c7991ddee59155c58145e12ad81310b509bd2e25c5b0247b3"}, + {file = "ruff-0.9.0-py3-none-win_amd64.whl", hash = "sha256:0cbc0905d94d21305872f7f8224e30f4bbcd532bc21b2225b2446d8fc7220d19"}, + {file = "ruff-0.9.0-py3-none-win_arm64.whl", hash = "sha256:7b1148771c6ca88f820d761350a053a5794bc58e0867739ea93eb5e41ad978cd"}, + {file = "ruff-0.9.0.tar.gz", hash = "sha256:143f68fa5560ecf10fc49878b73cee3eab98b777fcf43b0e62d43d42f5ef9d8b"}, ] [[package]] name = "tomli" -version = "2.0.1" +version = "2.2.1" description = "A lil' TOML parser" -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] name = "tox" -version = "4.11.3" +version = "4.23.2" description = "tox is a generic virtualenv management and test command line tool" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "tox-4.11.3-py3-none-any.whl", hash = "sha256:599af5e5bb0cad0148ac1558a0b66f8fff219ef88363483b8d92a81e4246f28f"}, - {file = "tox-4.11.3.tar.gz", hash = "sha256:5039f68276461fae6a9452a3b2c7295798f00a0e92edcd9a3b78ba1a73577951"}, + {file = "tox-4.23.2-py3-none-any.whl", hash = "sha256:452bc32bb031f2282881a2118923176445bac783ab97c874b8770ab4c3b76c38"}, + {file = "tox-4.23.2.tar.gz", hash = "sha256:86075e00e555df6e82e74cfc333917f91ecb47ffbc868dcafbd2672e332f4a2c"}, ] [package.dependencies] -cachetools = ">=5.3.1" +cachetools = ">=5.5" chardet = ">=5.2" colorama = ">=0.4.6" -filelock = ">=3.12.3" -packaging = ">=23.1" -platformdirs = ">=3.10" -pluggy = ">=1.3" -pyproject-api = ">=1.6.1" +filelock = ">=3.16.1" +packaging = ">=24.1" +platformdirs = ">=4.3.6" +pluggy = ">=1.5" +pyproject-api = ">=1.8" tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} -virtualenv = ">=20.24.3" +typing-extensions = {version = ">=4.12.2", markers = "python_version < \"3.11\""} +virtualenv = ">=20.26.6" [package.extras] -docs = ["furo (>=2023.8.19)", "sphinx (>=7.2.4)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.24)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -testing = ["build[virtualenv] (>=0.10)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.1.1)", "devpi-process (>=1)", "diff-cover (>=7.7)", "distlib (>=0.3.7)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.18)", "psutil (>=5.9.5)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-xdist (>=3.3.1)", "re-assert (>=1.1)", "time-machine (>=2.12)", "wheel (>=0.41.2)"] +test = ["devpi-process (>=1.0.2)", "pytest (>=8.3.3)", "pytest-mock (>=3.14)"] [[package]] name = "tox-gh-actions" -version = "3.1.3" +version = "3.2.0" description = "Seamless integration of tox into GitHub Actions" -category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "tox-gh-actions-3.1.3.tar.gz", hash = "sha256:ffd4151fe8b62c6f401a2fc5a01317835d7ab380923f6e0d063c300750308328"}, - {file = "tox_gh_actions-3.1.3-py2.py3-none-any.whl", hash = "sha256:5954766fe2ed0e284f3cdc87535dfdf68d0f803f1011b17ff8cf52ed3156e6c1"}, + {file = "tox-gh-actions-3.2.0.tar.gz", hash = "sha256:ac6fa3b8da51bc90dd77985fd55f09e746c6558c55910c0a93d643045a2b0ccc"}, + {file = "tox_gh_actions-3.2.0-py2.py3-none-any.whl", hash = "sha256:821b66a4751a788fa3e9617bd796d696507b08c6e1d929ee4faefba06b73b694"}, ] [package.dependencies] @@ -412,38 +447,36 @@ testing = ["black", "devpi-process", "flake8 (>=6,<7)", "mypy", "pytest (>=7,<8) [[package]] name = "typing-extensions" -version = "4.8.0" +version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" -category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "virtualenv" -version = "20.24.5" +version = "20.28.1" description = "Virtual Python Environment builder" -category = "dev" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "virtualenv-20.24.5-py3-none-any.whl", hash = "sha256:b80039f280f4919c77b30f1c23294ae357c4c8701042086e3fc005963e4e537b"}, - {file = "virtualenv-20.24.5.tar.gz", hash = "sha256:e8361967f6da6fbdf1426483bfe9fca8287c242ac0bc30429905721cefbff752"}, + {file = "virtualenv-20.28.1-py3-none-any.whl", hash = "sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb"}, + {file = "virtualenv-20.28.1.tar.gz", hash = "sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329"}, ] [package.dependencies] distlib = ">=0.3.7,<1" filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<4" +platformdirs = ">=3.9.1,<5" [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] [metadata] lock-version = "2.0" -python-versions = '^3.9.0' -content-hash = "df79b3653e07ccf4d3f98d8787bdd305c79e13acd18766fb0c3e30a3f8c9c254" +python-versions = "^3.9.0" +content-hash = "a901c068613398cf70068ee526df785c227c07eae50ea491de95def2e51d0e62" diff --git a/pyproject.toml b/pyproject.toml index 75c692e..58cce92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,74 +1,74 @@ [build-system] -requires = ['poetry-core>=1.2.0', 'wheel',] -build-backend = 'poetry.core.masonry.api' +requires = ["poetry-core>=1.2.0", "wheel",] +build-backend = "poetry.core.masonry.api" [tool.coverage.run] branch = true relative_files = true omit = [ - '.tox/*', - 'tests/*', + ".tox/*", + "tests/*", ] [tool.coverage.report] exclude_lines = [ - 'pragma: not covered', - '@overload', - 'if TYPE_CHECKING:', + "pragma: not covered", + "@overload", + "if TYPE_CHECKING:", ] [tool.poetry] -name = 'iplib3' -version = '0.2.4' +name = "iplib3" +version = "0.2.4" description = "A modern, object-oriented approach to IP addresses." authors = ["Lari Liuhamo ",] maintainers = ["Lari Liuhamo ",] -include = ['CHANGELOG.md', 'LICENSE', 'py.typed',] -license = 'MIT' -readme = 'README.md' +include = ["CHANGELOG.md", "LICENSE", "py.typed",] +license = "MIT" +readme = "README.md" -homepage = 'https://pypi.org/project/iplib3/' -repository = 'https://github.com/Diapolo10/iplib3' -documentation = 'https://github.com/Diapolo10/iplib3/tree/main/docs' +homepage = "https://pypi.org/project/iplib3/" +repository = "https://github.com/Diapolo10/iplib3" +documentation = "https://github.com/Diapolo10/iplib3/tree/main/docs" keywords = [ - 'network', - 'networking', - 'ip', - 'ipaddress', - 'address', - 'python3', - 'pathlib', + "network", + "networking", + "ip", + "ipaddress", + "address", + "python3", + "pathlib", ] classifiers = [ - 'Development Status :: 4 - Beta', - 'Intended Audience :: Developers', - 'Intended Audience :: Information Technology', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - 'Programming Language :: Python :: Implementation :: CPython', - 'Programming Language :: Python :: Implementation :: PyPy', - 'Topic :: Internet', - 'Topic :: Software Development :: Libraries', - 'Typing :: Typed', + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Internet", + "Topic :: Software Development :: Libraries", + "Typing :: Typed", ] [tool.poetry.dependencies] -python = '^3.9.0' +python = "^3.9.0" [tool.poetry.group.dev.dependencies] -mypy = '^1.5.1' +mypy = "^1.5.1" [tool.poetry.group.linters] @@ -76,7 +76,7 @@ optional = true [tool.poetry.group.linters.dependencies] -ruff = '^0.0.290' +ruff = "^0.9.0" [tool.poetry.group.tests] @@ -84,16 +84,16 @@ optional = true [tool.poetry.group.tests.dependencies] -pytest = '^7.4.2' -pytest-cov = '^4.1.0' -tox = '^4.11.3' -tox-gh-actions = '^3.1.3' +pytest = "^7.4.4" +pytest-cov = "^4.1.0" +tox = "^4.23.2" +tox-gh-actions = "^3.2.0" [tool.poetry.urls] -"Source code" = 'https://github.com/Diapolo10/iplib3' -"Tracker" = 'https://github.com/Diapolo10/iplib3/issues' -"Changelog" = 'https://github.com/Diapolo10/iplib3/blob/main/CHANGELOG.md' +"Source code" = "https://github.com/Diapolo10/iplib3" +"Tracker" = "https://github.com/Diapolo10/iplib3/issues" +"Changelog" = "https://github.com/Diapolo10/iplib3/blob/main/CHANGELOG.md" [tool.pytest.ini_options] @@ -107,76 +107,43 @@ addopts = """ --ignore=docs/ """ testpaths = [ - 'tests', + "tests", ] [tool.ruff] -select = [ - 'A', # Builtins - 'ANN', # Annotations - 'ARG', # Unused arguments - 'B', # Bugbear - 'BLE', # Blind except - 'C4', # Comprehensions - 'C90', # mccabe - 'COM', # Commas - 'DTZ', # Datetimes - 'ERA', # Commented-out code - 'EXE', # Executable - 'G', # Logging format - 'I', # Isort - 'ICN', # Import conventions - 'INP', # Disallow PEP-420 (Implicit namespace packages) - 'INT', # gettext - 'ISC', # Implicit str concat - 'N', # PEP-8 Naming - 'NPY', # Numpy - 'PGH', # Pygrep hooks - 'PIE', # Unnecessary code - 'PL', # Pylint - 'PT', # Pytest - 'PTH', # Use Pathlib - 'PYI', # Stub files - 'RET', # Return - 'RUF', # Ruff - 'RSE', # Raise - 'S', # Bandit - 'SIM', # Code simplification - 'SLF', # Private member access - 'T20', # Print - 'TCH', # Type checking - 'TID', # Tidy imports - 'UP', # Pyupgrade - 'W', # Warnings - 'YTT', # sys.version +lint.select = ["ALL"] +lint.ignore = [ + "D203", # One blank line before class docstring + "D212", # Multi-line summary first line + "PLR0913", # Too many arguments + "Q000", # Single quotes found but double quotes preferred ] -ignore = [ - 'PLR0913', # Too many arguments -] -ignore-init-module-imports = true line-length = 120 +show-fixes = true +src = ["src",] +target-version = "py39" -[tool.ruff.mccabe] +[tool.ruff.lint.mccabe] # Unlike Flake8, default to a complexity level of 10. max-complexity = 10 -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] # https://beta.ruff.rs/docs/rules/ -'__init__.py' = ['F401','F403','F405',] -'tests/*' = ['ANN', 'ARG', 'INP001', 'S101',] +"__init__.py" = ["F401","F403","F405",] +"tests/*" = ["ANN", "ARG", "INP001", "S101",] -[tool.ruff.pylint] +[tool.ruff.lint.pylint] max-args = 15 max-branches = 20 max-returns = 10 max-statements = 80 -[tool.ruff.flake8-tidy-imports] +[tool.ruff.lint.flake8-tidy-imports] ban-relative-imports = "all" diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..c53faa3 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Tests, needed to import test case modules.""" diff --git a/tests/test_address.py b/tests/test_address.py index e1316f1..6de05c6 100644 --- a/tests/test_address.py +++ b/tests/test_address.py @@ -1,11 +1,10 @@ -"""Unit tests for iplib3.address""" +"""Unit tests for iplib3.address.""" import pytest - from iplib3 import IPAddress from iplib3.address import IPv6, PureAddress from iplib3.constants import IPV6_MAX_VALUE -from iplib3.constants.port import PORT_NUMBER_MIN_VALUE + from tests.test_cases_address import ( TEST_CASES_IPADDRESS, TEST_CASES_IPADDRESS_AS_IPV4, @@ -39,7 +38,7 @@ TEST_CASES_PURE_ADDRESS, ) def test_pure_address(pure_address): - """Test the PureAddress base class""" + """Test the PureAddress base class.""" assert pure_address @@ -48,7 +47,7 @@ def test_pure_address(pure_address): TEST_CASES_PURE_ADDRESS_EQUALITY, ) def test_pure_address_equality(address, input_address, excepted_output): - """Test PureAddress equality""" + """Test PureAddress equality.""" output = address == input_address assert output is excepted_output @@ -58,7 +57,7 @@ def test_pure_address_equality(address, input_address, excepted_output): TEST_CASES_PURE_ADDRESS_INEQUALITY, ) def test_pure_address_inequality(first_address, second_address): - """Test PureAddress inequality""" + """Test PureAddress inequality.""" assert first_address != second_address @@ -67,7 +66,7 @@ def test_pure_address_inequality(first_address, second_address): TEST_CASES_PURE_ADDRESS_NUM, ) def test_pure_address_num(address, excepted_output): - """Test PureAddress num property""" + """Test PureAddress num property.""" assert address.num == excepted_output @@ -76,13 +75,12 @@ def test_pure_address_num(address, excepted_output): TEST_CASES_PURE_ADDRESS_PORT, ) def test_pure_address_port(address, excepted_output): - """Test PureAddress port property""" + """Test PureAddress port property.""" assert address.port == excepted_output def test_pure_address_port_setter(): - """Test PureAddress port setter""" - + """Test PureAddress port setter.""" address = PureAddress() assert address.port is None @@ -98,6 +96,7 @@ def test_pure_address_port_setter(): TEST_CASES_PURE_ADDRESS_PORT_SETTER_ERROR, ) def test_pure_address_port_setter_error(value, error, match_message): + """Test the PureAddress port setter throws an error as expected.""" address = PureAddress() with pytest.raises(error, match=match_message): address.port = value @@ -108,7 +107,7 @@ def test_pure_address_port_setter_error(value, error, match_message): TEST_CASES_PURE_ADDRESS_AS_HEX, ) def test_pure_address_as_hex(pure_address, excepted_output): - """Test PureAddress hex output""" + """Test PureAddress hex output.""" assert pure_address.as_hex == excepted_output @@ -117,7 +116,7 @@ def test_pure_address_as_hex(pure_address, excepted_output): TEST_CASES_PURE_ADDRESS_NUM_TO_IPV4, ) def test_pure_address_num_to_ipv4(pure_address, excepted_output): - """Test PureAddress num to IPv4 string conversion""" + """Test PureAddress num to IPv4 string conversion.""" assert pure_address.num_to_ipv4() == excepted_output @@ -126,7 +125,7 @@ def test_pure_address_num_to_ipv4(pure_address, excepted_output): TEST_CASES_PURE_ADDRESS_NUM_TO_IPV6, ) def test_pure_address_num_to_ipv6(pure_address, excepted_output): - """Test PureAddress num to IPv6 string conversion""" + """Test PureAddress num to IPv6 string conversion.""" assert pure_address.num_to_ipv6() == excepted_output @@ -135,7 +134,7 @@ def test_pure_address_num_to_ipv6(pure_address, excepted_output): TEST_CASES_PURE_ADDRESS_NUM_TO_IPV6_NO_SHORTENING, ) def test_pure_address_num_to_ipv6_no_shortening(pure_address, excepted_output): - """Test PureAddress num to IPv6 string conversion without shortening""" + """Test PureAddress num to IPv6 string conversion without shortening.""" assert pure_address.num_to_ipv6(shorten=False) == excepted_output @@ -144,16 +143,13 @@ def test_pure_address_num_to_ipv6_no_shortening(pure_address, excepted_output): TEST_CASES_PURE_ADDRESS_NUM_TO_IPV6_REMOVE_ZEROS, ) def test_pure_address_num_to_ipv6_remove_zeroes(pure_address, excepted_output): - """Test PureAddress num to IPv6 string conversion with empty segment removal""" + """Test PureAddress num to IPv6 string conversion with empty segment removal.""" assert pure_address.num_to_ipv6(remove_zeroes=True) == excepted_output def test_pure_address_num_to_ipv6_remove_zeroes_no_shortening(): - """ - Test PureAddress num to IPv6 string conversion without - shortening but segment removal applied - """ - assert PureAddress(0xBADC_0FFE_E0DD_F00D).num_to_ipv6(shorten=False, remove_zeroes=True) == '::BADC:0FFE:E0DD:F00D' + """Test PureAddress num to IPv6 string conversion without shortening but segment removal applied.""" + assert PureAddress(0xBADC_0FFE_E0DD_F00D).num_to_ipv6(shorten=False, remove_zeroes=True) == "::BADC:0FFE:E0DD:F00D" @pytest.mark.parametrize( @@ -161,7 +157,7 @@ def test_pure_address_num_to_ipv6_remove_zeroes_no_shortening(): TEST_CASES_IPADDRESS, ) def test_ipaddress(ip_address, excepted_instance): - """Test the IPAddress class""" + """Test the IPAddress class.""" assert isinstance(ip_address, excepted_instance) @@ -170,7 +166,7 @@ def test_ipaddress(ip_address, excepted_instance): TEST_CASES_IPADDRESS_EQUALITY, ) def test_ipaddress_equality(ip_address, excepted_output): - """Test IPAddress equality""" + """Test IPAddress equality.""" assert ip_address == excepted_output @@ -179,11 +175,12 @@ def test_ipaddress_equality(ip_address, excepted_output): TEST_CASES_IPADDRESS_STRING, ) def test_ipaddress_string(ip_address, excepted_output): - """Test IPAddress string representation""" + """Test IPAddress string representation.""" assert str(ip_address) == excepted_output def test_ipaddress_string_error(): + """Test converting an invalid IP address to a string.""" with pytest.raises(ValueError, match="No valid address representation exists"): str(IPAddress(IPV6_MAX_VALUE + 1)) @@ -193,6 +190,7 @@ def test_ipaddress_string_error(): TEST_CASES_IPADDRESS_REPR, ) def test_ipaddress_repr(ip_address, excepted_output): + """Test IPAddress repr output matches expectations.""" assert repr(ip_address) == excepted_output @@ -201,7 +199,7 @@ def test_ipaddress_repr(ip_address, excepted_output): TEST_CASES_IPADDRESS_AS_IPV4, ) def test_ipaddress_as_ipv4(ip_address, excepted_instance): - """Test the IPAddress IPv4 constructor""" + """Test the IPAddress IPv4 constructor.""" assert isinstance(ip_address.as_ipv4, excepted_instance) @@ -210,15 +208,16 @@ def test_ipaddress_as_ipv4(ip_address, excepted_instance): TEST_CASES_IPADDRESS_AS_IPV6, ) def test_ipaddress_as_ipv6(ip_address, excepted_instance): - """Test the IPAddress IPv6 constructor""" + """Test the IPAddress IPv6 constructor.""" assert isinstance(ip_address.as_ipv6, excepted_instance) @pytest.mark.parametrize( - "input_ipv4", TEST_CASES_IPV4, + "input_ipv4", + TEST_CASES_IPV4, ) def test_ipv4(input_ipv4): - """Test the IPv4 class""" + """Test the IPv4 class.""" assert input_ipv4 @@ -227,7 +226,7 @@ def test_ipv4(input_ipv4): TEST_CASES_IPV4_STRING, ) def test_ipv4_string(input_ipv4, excepted_output): - """Test IPv4 string representation""" + """Test IPv4 string representation.""" assert str(input_ipv4) == excepted_output @@ -236,15 +235,16 @@ def test_ipv4_string(input_ipv4, excepted_output): TEST_CASES_IPV4_IPV4_TO_NUM, ) def test_ipv4_ipv4_to_num(input_ipv4, excepted_output): - """Test IPv4 to num conversion""" + """Test IPv4 to num conversion.""" assert input_ipv4._ipv4_to_num() == excepted_output # noqa: SLF001 @pytest.mark.parametrize( - "input_ipv6", TEST_CASES_IPV6, + "input_ipv6", + TEST_CASES_IPV6, ) def test_ipv6(input_ipv6): - """Test the IPv6 class""" + """Test the IPv6 class.""" assert input_ipv6 @@ -253,7 +253,7 @@ def test_ipv6(input_ipv6): TEST_CASES_IPV6_STRING, ) def test_ipv6_string(input_ipv6, excepted_output): - """Test IPv6 string representation""" + """Test IPv6 string representation.""" assert str(input_ipv6) == excepted_output @@ -262,7 +262,7 @@ def test_ipv6_string(input_ipv6, excepted_output): TEST_CASES_IPV6_IPV6_TO_NUM, ) def test_ipv6_ipv6_to_num(input_ipv6, excepted_output): - """Test IPv6 to num conversion""" + """Test IPv6 to num conversion.""" assert IPv6(input_ipv6)._ipv6_to_num() == excepted_output # noqa: SLF001 @@ -271,5 +271,6 @@ def test_ipv6_ipv6_to_num(input_ipv6, excepted_output): TEST_CASES_IPV6_IPV6_TO_NUM_ERRORS, ) def test_ipv6_ipv6_to_num_errors(input_ipv6, error, match_message): + """Test IPv6 to int conversion throws expected errors.""" with pytest.raises(error, match=match_message): IPv6(input_ipv6)._ipv6_to_num() # noqa: SLF001 diff --git a/tests/test_cases_address.py b/tests/test_cases_address.py index 04fbc57..78fe7ef 100644 --- a/tests/test_cases_address.py +++ b/tests/test_cases_address.py @@ -1,3 +1,5 @@ +"""Test cases for addresses.""" + from iplib3 import IPAddress, IPv4, IPv6 from iplib3.address import PureAddress from iplib3.constants import ( @@ -19,7 +21,7 @@ (PureAddress(0xDE_AD_BE_EF), PureAddress(0xDE_AD_BE_EF), True), (PureAddress(0xDE_AD_BE_EF), PureAddress(0xDE_AD_BE_EF, 80), False), (PureAddress(0xDE_AD_BE_EF), 0xDE_AD_BE_EF, False), - (PureAddress(0xDE_AD_BE_EF), 'cheese', False), + (PureAddress(0xDE_AD_BE_EF), "cheese", False), ] TEST_CASES_PURE_ADDRESS_INEQUALITY = [ @@ -46,59 +48,59 @@ TEST_CASES_PURE_ADDRESS_PORT_SETTER_ERROR = [ (3.14, TypeError, "Port "), - ('80', TypeError, "Port "), + ("80", TypeError, "Port "), (PORT_NUMBER_MAX_VALUE + 1, ValueError, "Port number "), (PORT_NUMBER_MIN_VALUE - 1, ValueError, "Port number "), ] TEST_CASES_PURE_ADDRESS_AS_HEX = [ - (PureAddress(0xDE_AD_BE_EF), '0xDEADBEEF'), - (PureAddress(0x8B_AD_F0_0D), '0x8BADF00D'), - (PureAddress(0xDE_AD_C0_DE), '0xDEADC0DE'), + (PureAddress(0xDE_AD_BE_EF), "0xDEADBEEF"), + (PureAddress(0x8B_AD_F0_0D), "0x8BADF00D"), + (PureAddress(0xDE_AD_C0_DE), "0xDEADC0DE"), ] TEST_CASES_PURE_ADDRESS_NUM_TO_IPV4 = [ - (PureAddress(IPV4_LOCALHOST), '127.0.0.1'), + (PureAddress(IPV4_LOCALHOST), "127.0.0.1"), ] TEST_CASES_PURE_ADDRESS_NUM_TO_IPV6 = [ - (PureAddress(IPV6_LOCALHOST), '0:0:0:0:0:0:0:1'), - (PureAddress(0xDEAD_C0DE_1057_BE17), '0:0:0:0:DEAD:C0DE:1057:BE17'), - (PureAddress(0xBADC_0FFE_E0DD_F00D), '0:0:0:0:BADC:FFE:E0DD:F00D'), + (PureAddress(IPV6_LOCALHOST), "0:0:0:0:0:0:0:1"), + (PureAddress(0xDEAD_C0DE_1057_BE17), "0:0:0:0:DEAD:C0DE:1057:BE17"), + (PureAddress(0xBADC_0FFE_E0DD_F00D), "0:0:0:0:BADC:FFE:E0DD:F00D"), ] TEST_CASES_PURE_ADDRESS_NUM_TO_IPV6_NO_SHORTENING = [ - (PureAddress(IPV6_LOCALHOST), '0000:0000:0000:0000:0000:0000:0000:0001'), - (PureAddress(0xDEAD_C0DE_1057_BE17), '0000:0000:0000:0000:DEAD:C0DE:1057:BE17'), - (PureAddress(0xBADC_0FFE_E0DD_F00D), '0000:0000:0000:0000:BADC:0FFE:E0DD:F00D'), + (PureAddress(IPV6_LOCALHOST), "0000:0000:0000:0000:0000:0000:0000:0001"), + (PureAddress(0xDEAD_C0DE_1057_BE17), "0000:0000:0000:0000:DEAD:C0DE:1057:BE17"), + (PureAddress(0xBADC_0FFE_E0DD_F00D), "0000:0000:0000:0000:BADC:0FFE:E0DD:F00D"), ] TEST_CASES_PURE_ADDRESS_NUM_TO_IPV6_REMOVE_ZEROS = [ - (PureAddress(IPV6_LOCALHOST), '::1'), - (PureAddress(0xDEAD_C0DE_1057_BE17), '::DEAD:C0DE:1057:BE17'), - (PureAddress(0xBADC_0FFE_E0DD_F00D), '::BADC:FFE:E0DD:F00D'), + (PureAddress(IPV6_LOCALHOST), "::1"), + (PureAddress(0xDEAD_C0DE_1057_BE17), "::DEAD:C0DE:1057:BE17"), + (PureAddress(0xBADC_0FFE_E0DD_F00D), "::BADC:FFE:E0DD:F00D"), ] TEST_CASES_IPADDRESS = [ (IPAddress(), IPAddress), (IPAddress(IPV4_LOCALHOST), IPAddress), - (IPAddress('127.0.0.1'), IPv4), - (IPAddress('::DEAD:BEEF'), IPv6), + (IPAddress("127.0.0.1"), IPv4), # type: ignore[arg-type] + (IPAddress("::DEAD:BEEF"), IPv6), # type: ignore[arg-type] (IPAddress(IPV4_LOCALHOST, 80), IPAddress), - (IPAddress('127.0.0.1', 80), IPv4), - (IPAddress('::DEAD:BEEF', 80), IPv6), + (IPAddress("127.0.0.1", 80), IPv4), # type: ignore[arg-type] + (IPAddress("::DEAD:BEEF", 80), IPv6), # type: ignore[arg-type] ] TEST_CASES_IPADDRESS_EQUALITY = [ (IPAddress(IPV4_LOCALHOST), IPAddress(IPV4_LOCALHOST)), - (IPAddress(IPV4_LOCALHOST), '127.0.0.1'), + (IPAddress(IPV4_LOCALHOST), "127.0.0.1"), (IPAddress(IPV4_LOCALHOST), PureAddress(IPV4_LOCALHOST)), ] TEST_CASES_IPADDRESS_STRING = [ - (IPAddress(), '127.0.0.1'), - (IPAddress(IPV4_LOCALHOST), '127.0.0.1'), - (IPAddress(0xDEAD_DEAD_BEEF), '0:0:0:0:0:DEAD:DEAD:BEEF'), + (IPAddress(), "127.0.0.1"), + (IPAddress(IPV4_LOCALHOST), "127.0.0.1"), + (IPAddress(0xDEAD_DEAD_BEEF), "0:0:0:0:0:DEAD:DEAD:BEEF"), ] TEST_CASES_IPADDRESS_REPR = [ @@ -109,70 +111,70 @@ TEST_CASES_IPADDRESS_AS_IPV4 = [ (IPAddress(), IPv4), - (IPAddress('127.0.0.1'), IPv4), - (IPAddress('::DEAD:BEEF'), IPv4), + (IPAddress("127.0.0.1"), IPv4), # type: ignore[arg-type] + (IPAddress("::DEAD:BEEF"), IPv4), # type: ignore[arg-type] ] TEST_CASES_IPADDRESS_AS_IPV6 = [ (IPAddress(), IPv6), - (IPAddress('127.0.0.1'), IPv6), - (IPAddress('::DEAD:BEEF'), IPv6), + (IPAddress("127.0.0.1"), IPv6), # type: ignore[arg-type] + (IPAddress("::DEAD:BEEF"), IPv6), # type: ignore[arg-type] ] TEST_CASES_IPV4 = [ IPv4(), - IPv4('127.0.0.1'), - IPv4('127.0.0.1:80'), - IPv4('127.0.0.1', 80), - IPv4('127.0.0.1:80', 8080), - IPv4('127.0.0.1', port_num=80), + IPv4("127.0.0.1"), + IPv4("127.0.0.1:80"), + IPv4("127.0.0.1", 80), + IPv4("127.0.0.1:80", 8080), + IPv4("127.0.0.1", port_num=80), ] TEST_CASES_IPV4_STRING = [ - (IPv4(), '127.0.0.1'), - (IPv4('127.0.0.1'), '127.0.0.1'), - (IPv4('127.0.0.1:80'), '127.0.0.1:80'), - (IPv4('127.0.0.1', 80), '127.0.0.1:80'), - (IPv4('127.0.0.1:80', 8080), '127.0.0.1:8080'), + (IPv4(), "127.0.0.1"), + (IPv4("127.0.0.1"), "127.0.0.1"), + (IPv4("127.0.0.1:80"), "127.0.0.1:80"), + (IPv4("127.0.0.1", 80), "127.0.0.1:80"), + (IPv4("127.0.0.1:80", 8080), "127.0.0.1:8080"), ] TEST_CASES_IPV4_IPV4_TO_NUM = [ (IPv4(), IPV4_LOCALHOST), - (IPv4('127.0.0.1'), IPV4_LOCALHOST), - (IPv4('192.168.0.1'), 0xC0_A8_00_01), + (IPv4("127.0.0.1"), IPV4_LOCALHOST), + (IPv4("192.168.0.1"), 0xC0_A8_00_01), ] TEST_CASES_IPV6 = [ IPv6(), - IPv6('2606:4700:4700::1111'), - IPv6('2606:4700:4700::1111', 80), - IPv6('[2606:4700:4700::1111]:80'), - IPv6('[2606:4700:4700::1111]:80', 8080), + IPv6("2606:4700:4700::1111"), + IPv6("2606:4700:4700::1111", 80), + IPv6("[2606:4700:4700::1111]:80"), + IPv6("[2606:4700:4700::1111]:80", 8080), ] TEST_CASES_IPV6_STRING = [ - (IPv6(), '0:0:0:0:0:0:0:1'), - (IPv6('2606:4700:4700::1111'), '2606:4700:4700::1111'), - (IPv6('2606:4700:4700::1111', 80), '[2606:4700:4700::1111]:80'), - (IPv6('[2606:4700:4700::1111]:80'), '[2606:4700:4700::1111]:80'), - (IPv6('[2606:4700:4700::1111]:80', 8080), '[2606:4700:4700::1111]:8080'), - (IPv6('2606:4700:4700::1111', port_num=80), '[2606:4700:4700::1111]:80'), + (IPv6(), "0:0:0:0:0:0:0:1"), + (IPv6("2606:4700:4700::1111"), "2606:4700:4700::1111"), + (IPv6("2606:4700:4700::1111", 80), "[2606:4700:4700::1111]:80"), + (IPv6("[2606:4700:4700::1111]:80"), "[2606:4700:4700::1111]:80"), + (IPv6("[2606:4700:4700::1111]:80", 8080), "[2606:4700:4700::1111]:8080"), + (IPv6("2606:4700:4700::1111", port_num=80), "[2606:4700:4700::1111]:80"), ] TEST_CASES_IPV6_IPV6_TO_NUM = [ (None, IPV6_LOCALHOST), - ('70::', 0x70_0000_0000_0000_0000_0000_0000_0000), + ("70::", 0x70_0000_0000_0000_0000_0000_0000_0000), ] TEST_CASES_IPV6_IPV6_TO_NUM_ERRORS = [ # Two zero-skips - ('::DE::AD', ValueError, "Invalid IPv6 address format; only one zero-skip allowed"), + ("::DE::AD", ValueError, "Invalid IPv6 address format; only one zero-skip allowed"), # Invalid hex literal - ('::H07:AF', ValueError, "Invalid IPv6 address format; address contains invalid characters"), + ("::H07:AF", ValueError, "Invalid IPv6 address format; address contains invalid characters"), # Too many segments - ('1:1:1:1:1:1:1:1:1', ValueError, "Invalid IPv6 address format; too many segments "), + ("1:1:1:1:1:1:1:1:1", ValueError, "Invalid IPv6 address format; too many segments "), # Segment value too high - ('::7:FFFFF', ValueError, "Invalid IPv6 address format; segment max value "), + ("::7:FFFFF", ValueError, "Invalid IPv6 address format; segment max value "), # Segment value too low (negative) - ('::7:-34', ValueError, "Invalid IPv6 address format; segment min value "), + ("::7:-34", ValueError, "Invalid IPv6 address format; segment min value "), ] diff --git a/tests/test_cases_subnet.py b/tests/test_cases_subnet.py index 4b738f5..bae41b0 100644 --- a/tests/test_cases_subnet.py +++ b/tests/test_cases_subnet.py @@ -1,3 +1,5 @@ +"""Test cases for subnets.""" + from iplib3.constants import ( IPV4_MAX_SUBNET_VALUE, IPV4_MIN_SUBNET_VALUE, @@ -12,14 +14,14 @@ ] TEST_CASES_PURE_SUBNET_MASK_STRING = [ - (PureSubnetMask(), '0', 'str'), - (PureSubnetMask(), "iplib3.PureSubnetMask('0')", 'repr'), + (PureSubnetMask(), "0", "str"), + (PureSubnetMask(), "iplib3.PureSubnetMask('0')", "repr"), ] TEST_CASES_PURE_SUBNET_MASK_EQUALITY = [ (PureSubnetMask(), PureSubnetMask()), (PureSubnetMask(), IPV4_MIN_SUBNET_VALUE), - (PureSubnetMask(), '0'), + (PureSubnetMask(), "0"), ] TEST_CASES_PURE_SUBNET_MASK_INEQUALITY = [ @@ -30,12 +32,12 @@ TEST_CASES_SUBNET_MASK_SUBNET_TYPE = [ (SubnetMask(), SubnetType.IPV6), - (SubnetMask('255.255.255.0'), SubnetType.IPV4), + (SubnetMask("255.255.255.0"), SubnetType.IPV4), ] TEST_CASES_SUBNET_MASK_SUBNET_LENGTH = [ - ('255.255.255.255.128', ValueError, 'Subnet value not valid;'), - ('255.255.128', ValueError, 'Subnet value not valid;'), + ("255.255.255.255.128", ValueError, "Subnet value not valid;"), + ("255.255.128", ValueError, "Subnet value not valid;"), ] TEST_CASES_SUBNET_MASK_STRING = [ @@ -46,24 +48,24 @@ TEST_CASES_SUBNET_MASK_SUBNET_TO_NUM = [ (None, SubnetType.IPV6, None), (24, SubnetType.IPV6, 24), - ('24', SubnetType.IPV6, 24), + ("24", SubnetType.IPV6, 24), (None, SubnetType.IPV4, None), (24, SubnetType.IPV4, 24), - ('24', SubnetType.IPV4, 24), - ('255.255.128.0', SubnetType.IPV4, 17), + ("24", SubnetType.IPV4, 24), + ("255.255.128.0", SubnetType.IPV4, 17), ] TEST_CASES_SUBNET_MASK_SUBNET_TO_NUM_ERRORS = [ ([255, 255, 255, 0], SubnetType.IPV6, TypeError, "Invalid type for subnet value: "), - ('255.255.255.0', SubnetType.IPV6, ValueError, "IPv6-subnets don't use a string representation"), - ('3e2', SubnetType.IPV6, ValueError, "invalid literal "), + ("255.255.255.0", SubnetType.IPV6, ValueError, "IPv6-subnets don't use a string representation"), + ("3e2", SubnetType.IPV6, ValueError, "invalid literal "), (IPV4_MAX_SUBNET_VALUE + 1, SubnetType.IPV4, ValueError, "Subnet '"), (IPV6_MAX_SUBNET_VALUE + 1, SubnetType.IPV6, ValueError, "Subnet '"), - ('255.6.0.0', SubnetType.IPV4, ValueError, " is an invalid subnet mask"), + ("255.6.0.0", SubnetType.IPV4, ValueError, " is an invalid subnet mask"), ] TEST_CASES_SUBNET_MASK_PREFIX_TO_SUBNET_MASK = [ - (24, SubnetType.IPV4, '255.255.255.0'), + (24, SubnetType.IPV4, "255.255.255.0"), ] TEST_CASES_SUBNET_MASK_PREFIX_TO_SUBNET_MASK_ERRORS = [ diff --git a/tests/test_cases_validators.py b/tests/test_cases_validators.py index 51ce43b..d7bb2c7 100644 --- a/tests/test_cases_validators.py +++ b/tests/test_cases_validators.py @@ -1,3 +1,5 @@ +"""Test cases for validators.""" + from iplib3.constants import ( IPV4_MAX_SUBNET_VALUE, IPV4_MAX_VALUE, @@ -30,32 +32,32 @@ (PORT_NUMBER_MAX_VALUE + 1, False), (0xDEADBEEF, False), (22.0, False), - ('42', False), + ("42", False), ([1, 2, 3], False), ] TEST_CASES_IP_VALIDATOR = [ - ('128.0.0.1', True), + ("128.0.0.1", True), (0xDE_AD_BE_EF, True), - ('2606:4700:4700::1111', True), + ("2606:4700:4700::1111", True), (IPV6_MAX_VALUE, True), ] TEST_CASES_IPV4_VALIDATOR = [ - ('1.1.1.1', True, True), - ('0.0.0.0', True, True), # noqa: S104 - ('255.255.255.255', True, True), - ('192.168.0.1:8080', True, True), - ('12.123.234.345', True, False), - ('DE.AD.BE.EF', True, False), - ('12.23.34.45.56', False, False), - ('255,255,255,255', True, False), + ("1.1.1.1", True, True), + ("0.0.0.0", True, True), # noqa: S104 + ("255.255.255.255", True, True), + ("192.168.0.1:8080", True, True), + ("12.123.234.345", True, False), + ("DE.AD.BE.EF", True, False), + ("12.23.34.45.56", False, False), + ("255,255,255,255", True, False), ([127, 0, 0, 1], True, False), - ('1337.1337.1337.1337', True, False), - ('1.1.1.1:314159', True, False), - ('128.0.0.1:notaport', True, False), - ('1337.1337.1337.1337', False, True), - ('1337.1337.1337.1337:314159', False, True), + ("1337.1337.1337.1337", True, False), + ("1.1.1.1:314159", True, False), + ("128.0.0.1:notaport", True, False), + ("1337.1337.1337.1337", False, True), + ("1337.1337.1337.1337:314159", False, True), (25601440, True, True), (0xDEADBEEF, True, True), (25601440, False, True), @@ -68,23 +70,23 @@ ] TEST_CASES_IPV6_VALIDATOR = [ - ('0:0:0:0:0:0:0:0', True, True), - ('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF', True, True), - ('[0:0:0:0:0:0:0:0]:80', True, True), - ('[FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535', True, True), - ('::12', True, True), - ('314::', True, True), - ('2606:4700:4700::1111', True, True), - ('2606:4700:4700::10000', False, True), - ('2606:4700:4700::10000', True, False), - ('2606:4700::4700::1111', True, False), - ('2606:4700:4700::HACK', True, False), - ('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:0001', True, False), - ('2606:4700:4700:1111', True, False), + ("0:0:0:0:0:0:0:0", True, True), + ("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", True, True), + ("[0:0:0:0:0:0:0:0]:80", True, True), + ("[FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF]:65535", True, True), + ("::12", True, True), + ("314::", True, True), + ("2606:4700:4700::1111", True, True), + ("2606:4700:4700::10000", False, True), + ("2606:4700:4700::10000", True, False), + ("2606:4700::4700::1111", True, False), + ("2606:4700:4700::HACK", True, False), + ("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:0001", True, False), + ("2606:4700:4700:1111", True, False), ([2606, 4700, 4700, 0, 0, 0, 0, 1111], True, False), - ('[2606:4700:4700::1111]:notaport', True, False), - ('[2606:4700:4700::1111]:-1', True, False), - ('[2606:4700:4700::1111]:314159', True, False), + ("[2606:4700:4700::1111]:notaport", True, False), + ("[2606:4700:4700::1111]:-1", True, False), + ("[2606:4700:4700::1111]:314159", True, False), (IPV6_MIN_VALUE, True, True), (IPV6_MAX_VALUE, True, True), (IPV6_MIN_VALUE - 1, True, False), @@ -92,15 +94,15 @@ ] TEST_CASES_SUBNET_VALIDATOR = [ - ("255.0.0.0", 'ipv4', True), - ("255.0.0.0", 'IPV4', True), - ("255.0.0.0", 'ipv6', False), - ("255.255.255.128", 'ipv6', False), - ("255.128.0.0", 'ipv4', True), - (16, 'ipv6', True), - ("1.1.1.1", 'ipv4', False), - ("255.128.192.224", 'ipv4', False), - ("255.128.128.0", 'ipv4', False), + ("255.0.0.0", "ipv4", True), + ("255.0.0.0", "IPV4", True), + ("255.0.0.0", "ipv6", False), + ("255.255.255.128", "ipv6", False), + ("255.128.0.0", "ipv4", True), + (16, "ipv6", True), + ("1.1.1.1", "ipv4", False), + ("255.128.192.224", "ipv4", False), + ("255.128.128.0", "ipv4", False), ] TEST_CASES_IPV4_SUBNET_VALIDATOR = [ @@ -131,7 +133,7 @@ TEST_CASES_IPV4_SUBNET_VALIDATOR_ERRORS = [ ([1, 2, 3, 4], TypeError), - ({'1': 1}, TypeError), + ({"1": 1}, TypeError), ] TEST_CASES_IPV6_SUBNET_VALIDATOR = [ @@ -151,19 +153,19 @@ ] TEST_CASES_PORT_STRIPPER_IPV4 = [ - ("127.0.0.1:8080", 'ipv4', '127.0.0.1', 8080, True), - ("127.0.0.1:8080", 'IPv4', '127.0.0.1', 8080, True), - ("127.0.0.1:8080", 'IPV4', '127.0.0.1', 8080, True), - ("222.13.7.42:80", 'ipv4', '222.13.7.42', 80, True), - ("127.0.0.1", 'ipv4', '127.0.0.1', None, True), - ("222.13.7.42", 'ipv4', '222.13.7.42', None, True), + ("127.0.0.1:8080", "ipv4", "127.0.0.1", 8080, True), + ("127.0.0.1:8080", "IPv4", "127.0.0.1", 8080, True), + ("127.0.0.1:8080", "IPV4", "127.0.0.1", 8080, True), + ("222.13.7.42:80", "ipv4", "222.13.7.42", 80, True), + ("127.0.0.1", "ipv4", "127.0.0.1", None, True), + ("222.13.7.42", "ipv4", "222.13.7.42", None, True), ] TEST_CASES_PORT_STRIPPER_IPV6 = [ - ("[2606:4700:4700::1111]:8080", 'ipv6', '2606:4700:4700::1111', 8080, True), - ("[2606:4700:4700::1111]:8080", 'IPv6', '2606:4700:4700::1111', 8080, True), - ("[2606:4700:4700::1111]:8080", 'IPV6', '2606:4700:4700::1111', 8080, True), - ("[::DEAD:BEEF]:80", 'ipv6', '::DEAD:BEEF', 80, True), - ("2606:4700:4700::1111", 'ipv6', '2606:4700:4700::1111', None, True), - ("::DEAD:BEEF", 'ipv6', '::DEAD:BEEF', None, True), + ("[2606:4700:4700::1111]:8080", "ipv6", "2606:4700:4700::1111", 8080, True), + ("[2606:4700:4700::1111]:8080", "IPv6", "2606:4700:4700::1111", 8080, True), + ("[2606:4700:4700::1111]:8080", "IPV6", "2606:4700:4700::1111", 8080, True), + ("[::DEAD:BEEF]:80", "ipv6", "::DEAD:BEEF", 80, True), + ("2606:4700:4700::1111", "ipv6", "2606:4700:4700::1111", None, True), + ("::DEAD:BEEF", "ipv6", "::DEAD:BEEF", None, True), ] diff --git a/tests/test_subnet.py b/tests/test_subnet.py index d3607e9..d84f1bd 100644 --- a/tests/test_subnet.py +++ b/tests/test_subnet.py @@ -1,11 +1,11 @@ -"""Unit tests for iplib3.subnet""" +"""Unit tests for iplib3.subnet.""" import pytest - from iplib3.subnet import ( PureSubnetMask, SubnetMask, ) + from tests.test_cases_subnet import ( TEST_CASES_PURE_SUBNET_MASK_EQUALITY, TEST_CASES_PURE_SUBNET_MASK_INEQUALITY, @@ -22,7 +22,7 @@ def test_pure_subnet_mask(): - """Test the PureSubnetMask base class""" + """Test the PureSubnetMask base class.""" _ = PureSubnetMask() @@ -31,7 +31,7 @@ def test_pure_subnet_mask(): TEST_CASES_PURE_SUBNET_MASK_PREFIX_LENGTH, ) def test_pure_subnet_mask_prefix_length(subnet, prefix_length): - """Test PureSubnetMask prefix length""" + """Test PureSubnetMask prefix length.""" subnet._prefix_length = prefix_length # noqa: SLF001 assert subnet._prefix_length == prefix_length # noqa: SLF001 @@ -41,10 +41,10 @@ def test_pure_subnet_mask_prefix_length(subnet, prefix_length): TEST_CASES_PURE_SUBNET_MASK_STRING, ) def test_pure_subnet_mask_string(subnet, excepted_output, representation): - """Test PureSubnetMask string representation""" - if representation == 'str': + """Test PureSubnetMask string representation.""" + if representation == "str": assert str(subnet) == excepted_output - elif representation == 'repr': + elif representation == "repr": assert repr(subnet) == excepted_output @@ -53,7 +53,7 @@ def test_pure_subnet_mask_string(subnet, excepted_output, representation): TEST_CASES_PURE_SUBNET_MASK_EQUALITY, ) def test_pure_subnet_mask_equality(subnet, excepted_output): - """Test PureSubnetMask equality""" + """Test PureSubnetMask equality.""" assert subnet == excepted_output @@ -62,7 +62,7 @@ def test_pure_subnet_mask_equality(subnet, excepted_output): TEST_CASES_PURE_SUBNET_MASK_INEQUALITY, ) def test_pure_subnet_mask_inequality(subnet, excepted_output): - """Test PureSubnetMask inequality""" + """Test PureSubnetMask inequality.""" subnet._prefix_length = None # noqa: SLF001 assert subnet != excepted_output @@ -72,15 +72,16 @@ def test_pure_subnet_mask_inequality(subnet, excepted_output): TEST_CASES_SUBNET_MASK_SUBNET_TYPE, ) def test_subnet_mask_subnet_type(subnet, excepted_output): - """Test SubnetMask subnet type""" + """Test SubnetMask subnet type.""" assert subnet._subnet_type == excepted_output # noqa: SLF001 @pytest.mark.parametrize( - ('subnet', 'error', 'error_message'), - TEST_CASES_SUBNET_MASK_SUBNET_LENGTH, + ("subnet", "error", "error_message"), + TEST_CASES_SUBNET_MASK_SUBNET_LENGTH, ) def test_subnet_mask_subnet_length(subnet, error, error_message): + """Test the subnet length cannot be invalid.""" with pytest.raises(error, match=error_message): SubnetMask._ipv4_subnet_to_num(subnet) # noqa: SLF001 @@ -90,7 +91,7 @@ def test_subnet_mask_subnet_length(subnet, error, error_message): TEST_CASES_SUBNET_MASK_STRING, ) def test_subnet_mask_string(subnet, excepted_output): - """Test SubnetMask string representation""" + """Test SubnetMask string representation.""" assert repr(subnet) == excepted_output @@ -99,7 +100,7 @@ def test_subnet_mask_string(subnet, excepted_output): TEST_CASES_SUBNET_MASK_SUBNET_TO_NUM, ) def test_subnet_mask_subnet_to_num(subnet_mask, subnet_type, excepted_output): - """Test SubnetMask subnet to number converter""" + """Test SubnetMask subnet to number converter.""" assert SubnetMask._subnet_to_num(subnet_mask=subnet_mask, subnet_type=subnet_type) == excepted_output # noqa: SLF001 @@ -108,7 +109,7 @@ def test_subnet_mask_subnet_to_num(subnet_mask, subnet_type, excepted_output): TEST_CASES_SUBNET_MASK_SUBNET_TO_NUM_ERRORS, ) def test_subnet_mask_subnet_to_num_errors(subnet_mask, subnet_type, error, match_message): - """Test SubnetMask subnet to number converter errors""" + """Test SubnetMask subnet to number converter errors.""" with pytest.raises(error, match=match_message): SubnetMask._subnet_to_num(subnet_mask=subnet_mask, subnet_type=subnet_type) # noqa: SLF001 @@ -118,7 +119,7 @@ def test_subnet_mask_subnet_to_num_errors(subnet_mask, subnet_type, error, match TEST_CASES_SUBNET_MASK_PREFIX_TO_SUBNET_MASK, ) def test_subnet_mask_prefix_to_subnet_mask(prefix_length, subnet_type, excepted_output): - """Test SubnetMask number to mask converter""" + """Test SubnetMask number to mask converter.""" assert SubnetMask._prefix_to_subnet_mask(prefix_length=prefix_length, subnet_type=subnet_type) == excepted_output # noqa: SLF001 @@ -127,6 +128,6 @@ def test_subnet_mask_prefix_to_subnet_mask(prefix_length, subnet_type, excepted_ TEST_CASES_SUBNET_MASK_PREFIX_TO_SUBNET_MASK_ERRORS, ) def test_subnet_mask_prefix_to_subnet_mask_errors(prefix_length, subnet_type, error, match_message): - """Test SubnetMask number to mask converter""" + """Test SubnetMask number to mask converter.""" with pytest.raises(error, match=match_message): SubnetMask._prefix_to_subnet_mask(prefix_length=prefix_length, subnet_type=subnet_type) # noqa: SLF001 diff --git a/tests/test_validators.py b/tests/test_validators.py index bb51046..fc248e3 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -1,7 +1,6 @@ -"""Unit tests for iplib3.validators""" +"""Unit tests for iplib3.validators.""" import pytest - from iplib3.validators import ( _ipv4_subnet_validator, _ipv6_subnet_validator, @@ -12,6 +11,7 @@ port_validator, subnet_validator, ) + from tests.test_cases_validators import ( TEST_CASES_IP_VALIDATOR, TEST_CASES_IPV4_SUBNET_VALIDATOR, @@ -32,7 +32,7 @@ TEST_CASES_PORT_VALIDATOR, ) def test_port_validator(port_num, excepted_output): - """Test the port validator with None""" + """Test the port validator with None.""" assert port_validator(port_num=port_num) is excepted_output @@ -41,7 +41,7 @@ def test_port_validator(port_num, excepted_output): TEST_CASES_IP_VALIDATOR, ) def test_ip_validator(address, excepted_output): - """Test the generic IP address validator""" + """Test the generic IP address validator.""" assert ip_validator(address=address) is excepted_output @@ -50,6 +50,7 @@ def test_ip_validator(address, excepted_output): TEST_CASES_IPV4_VALIDATOR, ) def test_ipv4_validator(address, strict, excepted_output): + """Test the IPv4 validator.""" assert ipv4_validator(address=address, strict=strict) is excepted_output @@ -58,6 +59,7 @@ def test_ipv4_validator(address, strict, excepted_output): TEST_CASES_IPV6_VALIDATOR, ) def test_ipv6_validator(address, strict, excepted_output): + """Test the IPv6 validator.""" assert ipv6_validator(address=address, strict=strict) is excepted_output @@ -66,6 +68,7 @@ def test_ipv6_validator(address, strict, excepted_output): TEST_CASES_SUBNET_VALIDATOR, ) def test_subnet_validator(subnet, protocol, excepted_output): + """Test the generic subnet validator.""" assert subnet_validator(subnet=subnet, protocol=protocol) is excepted_output @@ -74,6 +77,7 @@ def test_subnet_validator(subnet, protocol, excepted_output): TEST_CASES_IPV4_SUBNET_VALIDATOR, ) def test_ipv4_subnet_validator(subnet, excepted_output): + """Test the IPv4 subnet validator.""" assert _ipv4_subnet_validator(subnet=subnet) is excepted_output @@ -82,7 +86,7 @@ def test_ipv4_subnet_validator(subnet, excepted_output): TEST_CASES_IPV4_SUBNET_VALIDATOR_ERRORS, ) def test_ipv4_subnet_validator_errors(subnet, error): - """Test the IPv4 subnet validator using invalid types""" + """Test the IPv4 subnet validator using invalid types.""" with pytest.raises(error): _ipv4_subnet_validator(subnet=subnet) @@ -92,6 +96,7 @@ def test_ipv4_subnet_validator_errors(subnet, error): TEST_CASES_IPV6_SUBNET_VALIDATOR, ) def test_ipv6_subnet_validator(subnet, excepted_output): + """Test the IPv6 subnet validator.""" assert _ipv6_subnet_validator(subnet=subnet) == excepted_output @@ -100,7 +105,7 @@ def test_ipv6_subnet_validator(subnet, excepted_output): TEST_CASES_IPV6_SUBNET_VALIDATOR_ERRORS, ) def test_ipv6_subnet_validator_errors(subnet, error): - """Test the IPv6 subnet validator using invalid types""" + """Test the IPv6 subnet validator using invalid types.""" with pytest.raises(error): _ipv6_subnet_validator(subnet=subnet) @@ -110,7 +115,7 @@ def test_ipv6_subnet_validator_errors(subnet, error): TEST_CASES_PORT_STRIPPER_IPV4, ) def test_port_stripper_ipv4(address, protocol, excepted_address, excepted_port, excepted_valid): - """Test the port stripper with IPv4""" + """Test the port stripper with IPv4.""" address, port, valid = _port_stripper(address=address, protocol=protocol) assert address == excepted_address assert port == excepted_port @@ -122,7 +127,7 @@ def test_port_stripper_ipv4(address, protocol, excepted_address, excepted_port, TEST_CASES_PORT_STRIPPER_IPV6, ) def test_port_stripper_ipv6(address, protocol, excepted_address, excepted_port, excepted_valid): - """Test the port stripper with IPv6""" + """Test the port stripper with IPv6.""" address, port, valid = _port_stripper(address=address, protocol=protocol) assert address == excepted_address assert port == excepted_port @@ -130,7 +135,6 @@ def test_port_stripper_ipv6(address, protocol, excepted_address, excepted_port, def test_port_stripper_invalid_protocol(): - """Test the port stripper for using invalid protocol""" - + """Test the port stripper for using invalid protocol.""" with pytest.raises(ValueError, match="Invalid subnet type"): - _port_stripper("127.0.0.1:8080", protocol='IPv9') # type: ignore # noqa: PGH003 + _port_stripper("127.0.0.1:8080", protocol="IPv9") # type: ignore # noqa: PGH003