pmd-net-addr 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pmd_net_addr/__init__.py +235 -0
- pmd_net_addr/address.py +293 -0
- pmd_net_addr/base.py +79 -0
- pmd_net_addr/click_types.py +384 -0
- pmd_net_addr/errors.py +363 -0
- pmd_net_addr/ip.py +69 -0
- pmd_net_addr/ip4_address.py +250 -0
- pmd_net_addr/ip4_ifaddr.py +110 -0
- pmd_net_addr/ip4_mask.py +114 -0
- pmd_net_addr/ip4_network.py +175 -0
- pmd_net_addr/ip4_wildcard.py +111 -0
- pmd_net_addr/ip6_address.py +630 -0
- pmd_net_addr/ip6_ifaddr.py +284 -0
- pmd_net_addr/ip6_mask.py +99 -0
- pmd_net_addr/ip6_network.py +162 -0
- pmd_net_addr/ip6_wildcard.py +111 -0
- pmd_net_addr/ip_address.py +125 -0
- pmd_net_addr/ip_ifaddr.py +197 -0
- pmd_net_addr/ip_mask.py +132 -0
- pmd_net_addr/ip_network.py +545 -0
- pmd_net_addr/ip_version.py +42 -0
- pmd_net_addr/ip_wildcard.py +121 -0
- pmd_net_addr/mac_address.py +190 -0
- pmd_net_addr/py.typed +0 -0
- pmd_net_addr-0.0.1.dist-info/METADATA +175 -0
- pmd_net_addr-0.0.1.dist-info/RECORD +29 -0
- pmd_net_addr-0.0.1.dist-info/WHEEL +5 -0
- pmd_net_addr-0.0.1.dist-info/licenses/LICENSE +622 -0
- pmd_net_addr-0.0.1.dist-info/top_level.txt +1 -0
pmd_net_addr/__init__.py
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
################################################################################
|
|
2
|
+
## ##
|
|
3
|
+
## PyTCP - Python TCP/IP stack ##
|
|
4
|
+
## Copyright (C) 2020-present Sebastian Majewski ##
|
|
5
|
+
## ##
|
|
6
|
+
## This program is free software: you can redistribute it and/or modify ##
|
|
7
|
+
## it under the terms of the GNU General Public License as published by ##
|
|
8
|
+
## the Free Software Foundation, either version 3 of the License, or ##
|
|
9
|
+
## (at your option) any later version. ##
|
|
10
|
+
## ##
|
|
11
|
+
## This program is distributed in the hope that it will be useful, ##
|
|
12
|
+
## but WITHOUT ANY WARRANTY; without even the implied warranty of ##
|
|
13
|
+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ##
|
|
14
|
+
## GNU General Public License for more details. ##
|
|
15
|
+
## ##
|
|
16
|
+
## You should have received a copy of the GNU General Public License ##
|
|
17
|
+
## along with this program. If not, see <https://www.gnu.org/licenses/>. ##
|
|
18
|
+
## ##
|
|
19
|
+
## Author's email: ccie18643@gmail.com ##
|
|
20
|
+
## Github repository: https://github.com/ccie18643/PyTCP ##
|
|
21
|
+
## ##
|
|
22
|
+
################################################################################
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
This package contains classes used to represent network addresses.
|
|
27
|
+
|
|
28
|
+
pmd_net_addr/__init__.py
|
|
29
|
+
|
|
30
|
+
ver 3.0.7
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from typing import TYPE_CHECKING
|
|
34
|
+
|
|
35
|
+
from pmd_net_addr.errors import (
|
|
36
|
+
IfAddrError,
|
|
37
|
+
IfAddrFormatError,
|
|
38
|
+
IfAddrSanityError,
|
|
39
|
+
Ip4AddressError,
|
|
40
|
+
Ip4AddressFormatError,
|
|
41
|
+
Ip4AddressSanityError,
|
|
42
|
+
Ip4IfAddrError,
|
|
43
|
+
Ip4IfAddrFormatError,
|
|
44
|
+
Ip4IfAddrSanityError,
|
|
45
|
+
Ip4MaskError,
|
|
46
|
+
Ip4MaskFormatError,
|
|
47
|
+
Ip4NetworkError,
|
|
48
|
+
Ip4NetworkFormatError,
|
|
49
|
+
Ip4NetworkSanityError,
|
|
50
|
+
Ip4WildcardError,
|
|
51
|
+
Ip4WildcardFormatError,
|
|
52
|
+
Ip6AddressError,
|
|
53
|
+
Ip6AddressFormatError,
|
|
54
|
+
Ip6AddressSanityError,
|
|
55
|
+
Ip6IfAddrError,
|
|
56
|
+
Ip6IfAddrFormatError,
|
|
57
|
+
Ip6IfAddrSanityError,
|
|
58
|
+
Ip6MaskError,
|
|
59
|
+
Ip6MaskFormatError,
|
|
60
|
+
Ip6NetworkError,
|
|
61
|
+
Ip6NetworkFormatError,
|
|
62
|
+
Ip6NetworkSanityError,
|
|
63
|
+
Ip6WildcardError,
|
|
64
|
+
Ip6WildcardFormatError,
|
|
65
|
+
IpAddressError,
|
|
66
|
+
IpAddressFormatError,
|
|
67
|
+
IpAddressSanityError,
|
|
68
|
+
IpMaskError,
|
|
69
|
+
IpMaskFormatError,
|
|
70
|
+
IpNetworkError,
|
|
71
|
+
IpNetworkFormatError,
|
|
72
|
+
IpNetworkSanityError,
|
|
73
|
+
IpWildcardError,
|
|
74
|
+
IpWildcardFormatError,
|
|
75
|
+
MacAddressError,
|
|
76
|
+
MacAddressFormatError,
|
|
77
|
+
MacAddressSanityError,
|
|
78
|
+
NetAddrError,
|
|
79
|
+
)
|
|
80
|
+
from pmd_net_addr.ip4_address import IP4__ADDRESS_LEN, Ip4Address
|
|
81
|
+
from pmd_net_addr.ip4_ifaddr import Ip4IfAddr
|
|
82
|
+
from pmd_net_addr.ip4_mask import Ip4Mask
|
|
83
|
+
from pmd_net_addr.ip4_network import Ip4Network
|
|
84
|
+
from pmd_net_addr.ip4_wildcard import Ip4Wildcard
|
|
85
|
+
from pmd_net_addr.ip6_address import IP6__ADDRESS_LEN, Ip6Address
|
|
86
|
+
from pmd_net_addr.ip6_ifaddr import Ip6IfAddr
|
|
87
|
+
from pmd_net_addr.ip6_mask import Ip6Mask
|
|
88
|
+
from pmd_net_addr.ip6_network import Ip6Network
|
|
89
|
+
from pmd_net_addr.ip6_wildcard import Ip6Wildcard
|
|
90
|
+
from pmd_net_addr.ip_address import IpAddress
|
|
91
|
+
from pmd_net_addr.ip_ifaddr import IfAddr
|
|
92
|
+
from pmd_net_addr.ip_mask import IpMask
|
|
93
|
+
from pmd_net_addr.ip_network import IpNetwork
|
|
94
|
+
from pmd_net_addr.ip_version import IpVersion
|
|
95
|
+
from pmd_net_addr.ip_wildcard import IpWildcard
|
|
96
|
+
from pmd_net_addr.mac_address import MAC__ADDRESS_LEN, MacAddress
|
|
97
|
+
|
|
98
|
+
# The 'click'-typed CLI helpers are an opt-in extra: importing
|
|
99
|
+
# 'pmd_net_addr' (or any value type) must not drag in 'click'. The
|
|
100
|
+
# names below are re-exported lazily via the module '__getattr__'
|
|
101
|
+
# so 'from pmd_net_addr import ClickTypeIp4Address' still works but
|
|
102
|
+
# imports 'click' only on first access. The TYPE_CHECKING block
|
|
103
|
+
# gives static checkers (mypy strict, with no_implicit_reexport)
|
|
104
|
+
# the real bindings, and listing the names in '__all__' marks
|
|
105
|
+
# them as the explicit public surface.
|
|
106
|
+
if TYPE_CHECKING:
|
|
107
|
+
from pmd_net_addr.click_types import (
|
|
108
|
+
ClickTypeIfAddr,
|
|
109
|
+
ClickTypeIp4Address,
|
|
110
|
+
ClickTypeIp4IfAddr,
|
|
111
|
+
ClickTypeIp4Network,
|
|
112
|
+
ClickTypeIp6Address,
|
|
113
|
+
ClickTypeIp6IfAddr,
|
|
114
|
+
ClickTypeIp6Network,
|
|
115
|
+
ClickTypeIpAddress,
|
|
116
|
+
ClickTypeIpNetwork,
|
|
117
|
+
ClickTypeMacAddress,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
_LAZY_CLICK_TYPES: frozenset[str] = frozenset(
|
|
121
|
+
{
|
|
122
|
+
"ClickTypeIfAddr",
|
|
123
|
+
"ClickTypeIp4Address",
|
|
124
|
+
"ClickTypeIp4IfAddr",
|
|
125
|
+
"ClickTypeIp4Network",
|
|
126
|
+
"ClickTypeIp6Address",
|
|
127
|
+
"ClickTypeIp6IfAddr",
|
|
128
|
+
"ClickTypeIp6Network",
|
|
129
|
+
"ClickTypeIpAddress",
|
|
130
|
+
"ClickTypeIpNetwork",
|
|
131
|
+
"ClickTypeMacAddress",
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def __getattr__(name: str, /) -> object:
|
|
137
|
+
"""
|
|
138
|
+
Lazily resolve the opt-in 'click'-typed CLI helpers so the
|
|
139
|
+
'click' import is deferred to first access.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
if name in _LAZY_CLICK_TYPES:
|
|
143
|
+
from pmd_net_addr import click_types
|
|
144
|
+
|
|
145
|
+
return getattr(click_types, name)
|
|
146
|
+
|
|
147
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def __dir__() -> list[str]:
|
|
151
|
+
"""
|
|
152
|
+
List the public names, including the lazily-exposed CLI
|
|
153
|
+
helpers.
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
return sorted(__all__)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
__version__: str = "0.0.1"
|
|
160
|
+
|
|
161
|
+
__all__ = [
|
|
162
|
+
"ClickTypeIfAddr",
|
|
163
|
+
"ClickTypeIp4Address",
|
|
164
|
+
"ClickTypeIp4IfAddr",
|
|
165
|
+
"ClickTypeIp4Network",
|
|
166
|
+
"ClickTypeIp6Address",
|
|
167
|
+
"ClickTypeIp6IfAddr",
|
|
168
|
+
"ClickTypeIp6Network",
|
|
169
|
+
"ClickTypeIpAddress",
|
|
170
|
+
"ClickTypeIpNetwork",
|
|
171
|
+
"ClickTypeMacAddress",
|
|
172
|
+
"IP4__ADDRESS_LEN",
|
|
173
|
+
"IP6__ADDRESS_LEN",
|
|
174
|
+
"IfAddr",
|
|
175
|
+
"IfAddrError",
|
|
176
|
+
"IfAddrFormatError",
|
|
177
|
+
"IfAddrSanityError",
|
|
178
|
+
"Ip4Address",
|
|
179
|
+
"Ip4AddressError",
|
|
180
|
+
"Ip4AddressFormatError",
|
|
181
|
+
"Ip4AddressSanityError",
|
|
182
|
+
"Ip4IfAddr",
|
|
183
|
+
"Ip4IfAddrError",
|
|
184
|
+
"Ip4IfAddrFormatError",
|
|
185
|
+
"Ip4IfAddrSanityError",
|
|
186
|
+
"Ip4Mask",
|
|
187
|
+
"Ip4MaskError",
|
|
188
|
+
"Ip4MaskFormatError",
|
|
189
|
+
"Ip4Network",
|
|
190
|
+
"Ip4NetworkError",
|
|
191
|
+
"Ip4NetworkFormatError",
|
|
192
|
+
"Ip4NetworkSanityError",
|
|
193
|
+
"Ip4Wildcard",
|
|
194
|
+
"Ip4WildcardError",
|
|
195
|
+
"Ip4WildcardFormatError",
|
|
196
|
+
"Ip6Address",
|
|
197
|
+
"Ip6AddressError",
|
|
198
|
+
"Ip6AddressFormatError",
|
|
199
|
+
"Ip6AddressSanityError",
|
|
200
|
+
"Ip6IfAddr",
|
|
201
|
+
"Ip6IfAddrError",
|
|
202
|
+
"Ip6IfAddrFormatError",
|
|
203
|
+
"Ip6IfAddrSanityError",
|
|
204
|
+
"Ip6Mask",
|
|
205
|
+
"Ip6MaskError",
|
|
206
|
+
"Ip6MaskFormatError",
|
|
207
|
+
"Ip6Network",
|
|
208
|
+
"Ip6NetworkError",
|
|
209
|
+
"Ip6NetworkFormatError",
|
|
210
|
+
"Ip6NetworkSanityError",
|
|
211
|
+
"Ip6Wildcard",
|
|
212
|
+
"Ip6WildcardError",
|
|
213
|
+
"Ip6WildcardFormatError",
|
|
214
|
+
"IpAddress",
|
|
215
|
+
"IpAddressError",
|
|
216
|
+
"IpAddressFormatError",
|
|
217
|
+
"IpAddressSanityError",
|
|
218
|
+
"IpMask",
|
|
219
|
+
"IpMaskError",
|
|
220
|
+
"IpMaskFormatError",
|
|
221
|
+
"IpNetwork",
|
|
222
|
+
"IpNetworkError",
|
|
223
|
+
"IpNetworkFormatError",
|
|
224
|
+
"IpNetworkSanityError",
|
|
225
|
+
"IpVersion",
|
|
226
|
+
"IpWildcard",
|
|
227
|
+
"IpWildcardError",
|
|
228
|
+
"IpWildcardFormatError",
|
|
229
|
+
"MAC__ADDRESS_LEN",
|
|
230
|
+
"MacAddress",
|
|
231
|
+
"MacAddressError",
|
|
232
|
+
"MacAddressFormatError",
|
|
233
|
+
"MacAddressSanityError",
|
|
234
|
+
"NetAddrError",
|
|
235
|
+
]
|
pmd_net_addr/address.py
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
################################################################################
|
|
2
|
+
## ##
|
|
3
|
+
## PyTCP - Python TCP/IP stack ##
|
|
4
|
+
## Copyright (C) 2020-present Sebastian Majewski ##
|
|
5
|
+
## ##
|
|
6
|
+
## This program is free software: you can redistribute it and/or modify ##
|
|
7
|
+
## it under the terms of the GNU General Public License as published by ##
|
|
8
|
+
## the Free Software Foundation, either version 3 of the License, or ##
|
|
9
|
+
## (at your option) any later version. ##
|
|
10
|
+
## ##
|
|
11
|
+
## This program is distributed in the hope that it will be useful, ##
|
|
12
|
+
## but WITHOUT ANY WARRANTY; without even the implied warranty of ##
|
|
13
|
+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ##
|
|
14
|
+
## GNU General Public License for more details. ##
|
|
15
|
+
## ##
|
|
16
|
+
## You should have received a copy of the GNU General Public License ##
|
|
17
|
+
## along with this program. If not, see <https://www.gnu.org/licenses/>. ##
|
|
18
|
+
## ##
|
|
19
|
+
## Author's email: ccie18643@gmail.com ##
|
|
20
|
+
## Github repository: https://github.com/ccie18643/PyTCP ##
|
|
21
|
+
## ##
|
|
22
|
+
################################################################################
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
This module contains network address base class.
|
|
27
|
+
|
|
28
|
+
pmd_net_addr/address.py
|
|
29
|
+
|
|
30
|
+
ver 3.0.7
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from abc import ABC, abstractmethod
|
|
34
|
+
from typing import ClassVar, Self, override
|
|
35
|
+
|
|
36
|
+
from pmd_net_addr.base import Base
|
|
37
|
+
from pmd_net_addr.errors import IpAddressSanityError, NetAddrError
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Address(Base, ABC):
|
|
41
|
+
"""
|
|
42
|
+
Network address support base class.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
__slots__ = ("_address",)
|
|
46
|
+
|
|
47
|
+
_address: int
|
|
48
|
+
|
|
49
|
+
# The address-family width in bytes, bound once per concrete
|
|
50
|
+
# leaf (class-level constant, same pattern as '_version').
|
|
51
|
+
# Hot paths ('_with_offset', '__format__', 'max_prefixlen')
|
|
52
|
+
# read this instead of 'len(memoryview(self))', which would
|
|
53
|
+
# allocate a fresh bytearray + memoryview on every call.
|
|
54
|
+
_address_len: ClassVar[int]
|
|
55
|
+
|
|
56
|
+
# The concrete value type's free-message sanity error,
|
|
57
|
+
# raised for operation-precondition / invalid-argument
|
|
58
|
+
# failures (pmd_net_addr raises only NetAddrError subclasses).
|
|
59
|
+
# Every concrete subclass overrides this with its specific
|
|
60
|
+
# Sanity error; the default is a NetAddrError-subclass
|
|
61
|
+
# safety net so a subclass that omits the override still
|
|
62
|
+
# honours the §7.1 "no bare builtin escapes" contract
|
|
63
|
+
# rather than raising AttributeError.
|
|
64
|
+
_sanity_error: ClassVar[type[NetAddrError]] = IpAddressSanityError
|
|
65
|
+
|
|
66
|
+
@abstractmethod
|
|
67
|
+
def __init__(
|
|
68
|
+
self,
|
|
69
|
+
address: Self | str | bytes | bytearray | memoryview | int | None = None,
|
|
70
|
+
/,
|
|
71
|
+
) -> None:
|
|
72
|
+
"""
|
|
73
|
+
Initialize the network address object. Concrete
|
|
74
|
+
subclasses bind the accepted input forms.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
raise NotImplementedError
|
|
78
|
+
|
|
79
|
+
def __int__(self) -> int:
|
|
80
|
+
"""
|
|
81
|
+
Get the network address as integer.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
return self._address
|
|
85
|
+
|
|
86
|
+
def _format_alt(self, format_spec: str, /) -> str | None:
|
|
87
|
+
"""
|
|
88
|
+
Render a type-specific textual format code, or None if
|
|
89
|
+
the code is not recognised by this address type. The
|
|
90
|
+
base type recognises none.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
@override
|
|
96
|
+
def __format__(self, format_spec: str, /) -> str:
|
|
97
|
+
"""
|
|
98
|
+
Format the address. A type-specific text code ('ex'
|
|
99
|
+
for the expanded IP form, 'hy' / 'ci' for MAC
|
|
100
|
+
notations) is rendered by '_format_alt'; 'b' / 'x' /
|
|
101
|
+
'X' yield the integer zero-padded to the
|
|
102
|
+
address-family bit width, accepting the '#' (radix
|
|
103
|
+
prefix) and '_' (4-digit grouping) modifiers; 'd'
|
|
104
|
+
(plain decimal) and 'n' (locale-aware decimal)
|
|
105
|
+
delegate verbatim to the stdlib integer formatter and
|
|
106
|
+
take no modifiers. Any other spec — including an empty
|
|
107
|
+
spec or a bare fill / align / width / precision with
|
|
108
|
+
no presentation code — is delegated to str(self), so
|
|
109
|
+
the canonical text form supports the full string
|
|
110
|
+
mini-language without a trailing 's'.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
if not format_spec or format_spec[-1] == "s":
|
|
114
|
+
return format(str(self), format_spec)
|
|
115
|
+
|
|
116
|
+
alt = self._format_alt(format_spec)
|
|
117
|
+
if alt is not None:
|
|
118
|
+
return alt
|
|
119
|
+
|
|
120
|
+
code = format_spec[-1]
|
|
121
|
+
flags = format_spec[:-1]
|
|
122
|
+
|
|
123
|
+
if code in {"b", "x", "X", "d", "n"}:
|
|
124
|
+
# 'b' / 'x' / 'X' are the address-family-width
|
|
125
|
+
# zero-padded radix forms ('#' radix-prefix and '_'
|
|
126
|
+
# 4-digit grouping modifiers apply); 'd' / 'n'
|
|
127
|
+
# delegate verbatim to the stdlib integer formatter
|
|
128
|
+
# ('d' plain decimal, 'n' locale-aware decimal) and
|
|
129
|
+
# take no modifiers.
|
|
130
|
+
if not ((code in {"b", "x", "X"} and not (set(flags) - {"#", "_"})) or (code in {"d", "n"} and not flags)):
|
|
131
|
+
raise type(self)._sanity_error(
|
|
132
|
+
f"Unknown format code {format_spec!r} for object of type {type(self).__name__!r}"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
if code in {"d", "n"}:
|
|
136
|
+
return format(self._address, code)
|
|
137
|
+
|
|
138
|
+
bits = self._address_len * 8
|
|
139
|
+
|
|
140
|
+
digit_width = bits if code == "b" else bits // 4
|
|
141
|
+
digits = format(self._address, f"0{digit_width}{code}")
|
|
142
|
+
|
|
143
|
+
if "_" in flags:
|
|
144
|
+
digits = "_".join(digits[i : i + 4] for i in range(0, len(digits), 4))
|
|
145
|
+
|
|
146
|
+
if "#" in flags:
|
|
147
|
+
digits = ("0b" if code == "b" else "0x") + digits
|
|
148
|
+
|
|
149
|
+
return digits
|
|
150
|
+
|
|
151
|
+
# No recognised custom code: the spec is a
|
|
152
|
+
# string-presentation spec (bare fill / align / width /
|
|
153
|
+
# precision, no trailing 's' required). Delegate to
|
|
154
|
+
# str(self), converting the builtin ValueError that an
|
|
155
|
+
# unknown presentation code raises into this type's
|
|
156
|
+
# sanity error (pmd_net_addr.md §7.1 — no bare builtin
|
|
157
|
+
# escapes; chain the cause so the offending code is
|
|
158
|
+
# greppable in the traceback).
|
|
159
|
+
try:
|
|
160
|
+
return format(str(self), format_spec)
|
|
161
|
+
except ValueError as error:
|
|
162
|
+
raise type(self)._sanity_error(
|
|
163
|
+
f"Unknown format code {format_spec!r} for object of type {type(self).__name__!r}"
|
|
164
|
+
) from error
|
|
165
|
+
|
|
166
|
+
@abstractmethod
|
|
167
|
+
def __buffer__(self, _: int) -> memoryview:
|
|
168
|
+
"""
|
|
169
|
+
Get the network address as a memoryview.
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
raise NotImplementedError
|
|
173
|
+
|
|
174
|
+
@override
|
|
175
|
+
def __eq__(self, other: object, /) -> bool:
|
|
176
|
+
"""
|
|
177
|
+
Compare the network address with another object.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
return other is self or (isinstance(other, type(self)) and self._address == other._address)
|
|
181
|
+
|
|
182
|
+
@override
|
|
183
|
+
def __hash__(self) -> int:
|
|
184
|
+
"""
|
|
185
|
+
Get the network address hash value.
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
return hash((type(self), self._address))
|
|
189
|
+
|
|
190
|
+
def __lt__(self, other: object, /) -> bool:
|
|
191
|
+
"""
|
|
192
|
+
Order the network address by its integer value. Ordering
|
|
193
|
+
across address types (e.g. IPv4 vs IPv6) is undefined and
|
|
194
|
+
raises TypeError.
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
if not isinstance(other, type(self)):
|
|
198
|
+
return NotImplemented
|
|
199
|
+
|
|
200
|
+
return self._address < other._address
|
|
201
|
+
|
|
202
|
+
def __le__(self, other: object, /) -> bool:
|
|
203
|
+
"""
|
|
204
|
+
Order the network address by its integer value. Ordering
|
|
205
|
+
across address types (e.g. IPv4 vs IPv6) is undefined and
|
|
206
|
+
raises TypeError.
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
if not isinstance(other, type(self)):
|
|
210
|
+
return NotImplemented
|
|
211
|
+
|
|
212
|
+
return self._address <= other._address
|
|
213
|
+
|
|
214
|
+
def __gt__(self, other: object, /) -> bool:
|
|
215
|
+
"""
|
|
216
|
+
Order the network address by its integer value. Ordering
|
|
217
|
+
across address types (e.g. IPv4 vs IPv6) is undefined and
|
|
218
|
+
raises TypeError.
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
if not isinstance(other, type(self)):
|
|
222
|
+
return NotImplemented
|
|
223
|
+
|
|
224
|
+
return self._address > other._address
|
|
225
|
+
|
|
226
|
+
def __ge__(self, other: object, /) -> bool:
|
|
227
|
+
"""
|
|
228
|
+
Order the network address by its integer value. Ordering
|
|
229
|
+
across address types (e.g. IPv4 vs IPv6) is undefined and
|
|
230
|
+
raises TypeError.
|
|
231
|
+
"""
|
|
232
|
+
|
|
233
|
+
if not isinstance(other, type(self)):
|
|
234
|
+
return NotImplemented
|
|
235
|
+
|
|
236
|
+
return self._address >= other._address
|
|
237
|
+
|
|
238
|
+
def _with_offset(self, delta: int, /) -> Self:
|
|
239
|
+
"""
|
|
240
|
+
Get this address shifted by an integer offset. An
|
|
241
|
+
out-of-range result is an invalid-operation outcome, not
|
|
242
|
+
a malformed literal, so it raises the address type's
|
|
243
|
+
sanity error naming the operation (pmd_net_addr.md §7.2).
|
|
244
|
+
"""
|
|
245
|
+
|
|
246
|
+
result = self._address + delta
|
|
247
|
+
|
|
248
|
+
if not 0 <= result <= (1 << (self._address_len * 8)) - 1:
|
|
249
|
+
raise type(self)._sanity_error(
|
|
250
|
+
f"{type(self).__name__} offset out of range: " f"{self} {'+' if delta >= 0 else '-'} {abs(delta)}"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
return type(self)(result)
|
|
254
|
+
|
|
255
|
+
def __add__(self, other: object, /) -> Self:
|
|
256
|
+
"""
|
|
257
|
+
Get the network address advanced by an integer offset.
|
|
258
|
+
An out-of-range result raises the address-type sanity
|
|
259
|
+
error.
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
if not isinstance(other, int):
|
|
263
|
+
return NotImplemented
|
|
264
|
+
|
|
265
|
+
return self._with_offset(other)
|
|
266
|
+
|
|
267
|
+
def __sub__(self, other: object, /) -> Self:
|
|
268
|
+
"""
|
|
269
|
+
Get the network address retreated by an integer offset.
|
|
270
|
+
An out-of-range result raises the address-type sanity
|
|
271
|
+
error.
|
|
272
|
+
"""
|
|
273
|
+
|
|
274
|
+
if not isinstance(other, int):
|
|
275
|
+
return NotImplemented
|
|
276
|
+
|
|
277
|
+
return self._with_offset(-other)
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
def unspecified(self) -> Self:
|
|
281
|
+
"""
|
|
282
|
+
Get the unspecified network address.
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
return type(self)()
|
|
286
|
+
|
|
287
|
+
@property
|
|
288
|
+
def is_unspecified(self) -> bool:
|
|
289
|
+
"""
|
|
290
|
+
Check if the network address is unspecified.
|
|
291
|
+
"""
|
|
292
|
+
|
|
293
|
+
return self._address == 0
|
pmd_net_addr/base.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
################################################################################
|
|
2
|
+
## ##
|
|
3
|
+
## PyTCP - Python TCP/IP stack ##
|
|
4
|
+
## Copyright (C) 2020-present Sebastian Majewski ##
|
|
5
|
+
## ##
|
|
6
|
+
## This program is free software: you can redistribute it and/or modify ##
|
|
7
|
+
## it under the terms of the GNU General Public License as published by ##
|
|
8
|
+
## the Free Software Foundation, either version 3 of the License, or ##
|
|
9
|
+
## (at your option) any later version. ##
|
|
10
|
+
## ##
|
|
11
|
+
## This program is distributed in the hope that it will be useful, ##
|
|
12
|
+
## but WITHOUT ANY WARRANTY; without even the implied warranty of ##
|
|
13
|
+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ##
|
|
14
|
+
## GNU General Public License for more details. ##
|
|
15
|
+
## ##
|
|
16
|
+
## You should have received a copy of the GNU General Public License ##
|
|
17
|
+
## along with this program. If not, see <https://www.gnu.org/licenses/>. ##
|
|
18
|
+
## ##
|
|
19
|
+
## Author's email: ccie18643@gmail.com ##
|
|
20
|
+
## Github repository: https://github.com/ccie18643/PyTCP ##
|
|
21
|
+
## ##
|
|
22
|
+
################################################################################
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
This module contains the base class for all NetAddr objects.
|
|
27
|
+
|
|
28
|
+
pmd_net_addr/base.py
|
|
29
|
+
|
|
30
|
+
ver 3.0.7
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from abc import ABC, abstractmethod
|
|
34
|
+
from typing import override
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Base(ABC):
|
|
38
|
+
"""
|
|
39
|
+
NetAddr base class.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
__slots__ = ()
|
|
43
|
+
|
|
44
|
+
@override
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def __str__(self) -> str:
|
|
47
|
+
"""
|
|
48
|
+
Get the network object log string.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
raise NotImplementedError
|
|
52
|
+
|
|
53
|
+
@override
|
|
54
|
+
def __repr__(self) -> str:
|
|
55
|
+
"""
|
|
56
|
+
Get the network object representation string.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
return f"{type(self).__name__}({str(self)!r})"
|
|
60
|
+
|
|
61
|
+
@override
|
|
62
|
+
@abstractmethod
|
|
63
|
+
def __eq__(self, other: object, /) -> bool:
|
|
64
|
+
"""
|
|
65
|
+
Check if two network objects are equal.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
raise NotImplementedError
|
|
69
|
+
|
|
70
|
+
@override
|
|
71
|
+
@abstractmethod
|
|
72
|
+
def __hash__(self) -> int:
|
|
73
|
+
"""
|
|
74
|
+
Get the network object hash value. Concrete value types
|
|
75
|
+
define this consistently with their own '__eq__' (every
|
|
76
|
+
subclass overrides it).
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
raise NotImplementedError
|