ipaddons 0.5.0__tar.gz → 0.6.1__tar.gz
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.
- {ipaddons-0.5.0 → ipaddons-0.6.1}/PKG-INFO +3 -2
- {ipaddons-0.5.0 → ipaddons-0.6.1}/pyproject.toml +6 -5
- {ipaddons-0.5.0 → ipaddons-0.6.1}/src/ipaddons/_version.py +2 -2
- {ipaddons-0.5.0 → ipaddons-0.6.1}/src/ipaddons/tools.py +11 -11
- {ipaddons-0.5.0 → ipaddons-0.6.1}/tests/test_ip_allocation.py +31 -5
- {ipaddons-0.5.0 → ipaddons-0.6.1}/.gitignore +0 -0
- {ipaddons-0.5.0 → ipaddons-0.6.1}/LICENSE +0 -0
- {ipaddons-0.5.0 → ipaddons-0.6.1}/README.md +0 -0
- {ipaddons-0.5.0 → ipaddons-0.6.1}/src/ipaddons/__init__.py +0 -0
- {ipaddons-0.5.0 → ipaddons-0.6.1}/src/ipaddons/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: ipaddons
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: Additional tools for the ipaddress standard library module
|
|
5
5
|
Project-URL: documentation, https://ipaddons.readthedocs.io/
|
|
6
6
|
Project-URL: homepage, https://gitlab.com/sebastianw/ipaddons/
|
|
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
18
19
|
Classifier: Topic :: Utilities
|
|
19
20
|
Requires-Python: >=3.11
|
|
20
21
|
Description-Content-Type: text/markdown
|
|
@@ -20,6 +20,7 @@ classifiers = [
|
|
|
20
20
|
"Programming Language :: Python :: 3.12",
|
|
21
21
|
"Programming Language :: Python :: 3.13",
|
|
22
22
|
"Programming Language :: Python :: 3.14",
|
|
23
|
+
"Programming Language :: Python :: 3.15",
|
|
23
24
|
"Topic :: Utilities",
|
|
24
25
|
]
|
|
25
26
|
dynamic = [ "version" ]
|
|
@@ -40,10 +41,10 @@ docs = [
|
|
|
40
41
|
]
|
|
41
42
|
|
|
42
43
|
[tool.hatch]
|
|
43
|
-
build.hooks.vcs.version-file = "src/ipaddons/_version.py"
|
|
44
|
-
build.targets.sdist.only-include = [ "src/ipaddons", "tests" ]
|
|
45
44
|
version.source = "vcs"
|
|
46
45
|
version.raw-options.local_scheme = "no-local-version"
|
|
46
|
+
build.hooks.vcs.version-file = "src/ipaddons/_version.py"
|
|
47
|
+
build.targets.sdist.only-include = [ "src/ipaddons", "tests" ]
|
|
47
48
|
|
|
48
49
|
[tool.uv]
|
|
49
50
|
[[tool.uv.index]]
|
|
@@ -119,16 +120,16 @@ lint.isort.required-imports = [ "from __future__ import annotations" ]
|
|
|
119
120
|
lint.future-annotations = true
|
|
120
121
|
|
|
121
122
|
[tool.pyproject-fmt]
|
|
122
|
-
max_supported_python = "3.14"
|
|
123
123
|
column_width = 120
|
|
124
124
|
indent = 4
|
|
125
|
+
max_supported_python = "3.15"
|
|
125
126
|
|
|
126
127
|
[tool.mypy]
|
|
128
|
+
exclude = [ "^src/[^/]+/_version\\.py$" ]
|
|
127
129
|
disallow_untyped_defs = true
|
|
128
130
|
pretty = true
|
|
129
131
|
show_error_codes = true
|
|
130
|
-
exclude = [ "^src/[^/]+/_version\\.py$" ]
|
|
131
132
|
|
|
132
133
|
[tool.pytest]
|
|
133
|
-
ini_options.addopts = "--color=yes"
|
|
134
134
|
ini_options.testpaths = [ "tests" ]
|
|
135
|
+
ini_options.addopts = "--color=yes"
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0,
|
|
21
|
+
__version__ = version = '0.6.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 6, 1)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -100,9 +100,10 @@ def free_ranges(superrange: NetworkRange, used_ranges: list[NetworkRange]) -> Ge
|
|
|
100
100
|
:return: An interator for free ranges.
|
|
101
101
|
"""
|
|
102
102
|
remaining_superrange = superrange
|
|
103
|
-
# Make
|
|
103
|
+
# Make used ranges unique and sort them. This is crucial when iterating over them, as we always remove them from the
|
|
104
|
+
# beginning of the (remaining) superrange.
|
|
104
105
|
used_ranges = list(dict.fromkeys(used_ranges))
|
|
105
|
-
used_ranges
|
|
106
|
+
used_ranges.sort(key=lambda x: (x[0], -x[1]))
|
|
106
107
|
for used_range in used_ranges:
|
|
107
108
|
if not range_overlap(superrange, used_range):
|
|
108
109
|
msg = f"{used_range} does not overlap with {remaining_superrange}"
|
|
@@ -114,7 +115,7 @@ def free_ranges(superrange: NetworkRange, used_ranges: list[NetworkRange]) -> Ge
|
|
|
114
115
|
# Subrange ends below or begins above remaining superrange, ignore.
|
|
115
116
|
continue
|
|
116
117
|
if remaining_superrange[0] == used_range[0]:
|
|
117
|
-
# Used range is at the beginning of remaining superrange, shrink
|
|
118
|
+
# Used range is at the beginning of remaining superrange, shrink the superrange to exclude the used range.
|
|
118
119
|
remaining_superrange = used_range[1] + 1, remaining_superrange[1]
|
|
119
120
|
continue
|
|
120
121
|
# There is a free range between the beginning of the remaining superrange and the current used range.
|
|
@@ -167,14 +168,13 @@ def net_size_iterator(
|
|
|
167
168
|
supernet_range = int(supernet[0]), int(supernet[-1])
|
|
168
169
|
if used_ranges is None:
|
|
169
170
|
used_ranges = []
|
|
170
|
-
for
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
yield s, s + cidr_size - 1
|
|
171
|
+
for free_range_start, free_range_end in free_ranges(supernet_range, used_ranges):
|
|
172
|
+
free_net_start = _ceildiv(free_range_start, cidr_size) * cidr_size
|
|
173
|
+
free_net_end = free_net_start + cidr_size - 1
|
|
174
|
+
while free_net_end <= free_range_end:
|
|
175
|
+
yield free_net_start, free_net_end
|
|
176
|
+
free_net_start += cidr_size
|
|
177
|
+
free_net_end += cidr_size
|
|
178
178
|
|
|
179
179
|
|
|
180
180
|
class _BaseIPAllocation(ABC, Generic[_N]):
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import ipaddress
|
|
4
|
-
from typing import Any, NamedTuple
|
|
4
|
+
from typing import Any, NamedTuple, overload
|
|
5
5
|
|
|
6
6
|
import pytest
|
|
7
7
|
|
|
@@ -14,6 +14,14 @@ class Net(NamedTuple):
|
|
|
14
14
|
range: tuple[int, int]
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
@overload
|
|
18
|
+
def make_nets(input_str: str) -> Net: ...
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@overload
|
|
22
|
+
def make_nets(input_str: list[str]) -> list[Net]: ...
|
|
23
|
+
|
|
24
|
+
|
|
17
25
|
def make_nets(input_str: str | list[str]) -> Net | list[Net]:
|
|
18
26
|
if isinstance(input_str, str):
|
|
19
27
|
single = True
|
|
@@ -31,6 +39,14 @@ def make_nets(input_str: str | list[str]) -> Net | list[Net]:
|
|
|
31
39
|
return ret
|
|
32
40
|
|
|
33
41
|
|
|
42
|
+
@overload
|
|
43
|
+
def net_attr(nets: Net, attribute: str) -> Any: ...
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@overload
|
|
47
|
+
def net_attr(nets: list[Net], attribute: str) -> list[Any]: ...
|
|
48
|
+
|
|
49
|
+
|
|
34
50
|
def net_attr(nets: Net | list[Net], attribute: str) -> Any | list[Any]:
|
|
35
51
|
if isinstance(nets, Net):
|
|
36
52
|
return getattr(nets, attribute)
|
|
@@ -105,7 +121,7 @@ def test_base_abstract_methods():
|
|
|
105
121
|
TypeError,
|
|
106
122
|
match=r"Can't instantiate abstract class TestAllocation without an implementation for abstract methods '_max_prefixlen', '_network_class', '_version'",
|
|
107
123
|
):
|
|
108
|
-
TestAllocation()
|
|
124
|
+
TestAllocation("10.0.0.0/8")
|
|
109
125
|
|
|
110
126
|
|
|
111
127
|
@pytest.mark.parametrize(
|
|
@@ -117,6 +133,16 @@ def test_base_abstract_methods():
|
|
|
117
133
|
("2001:db8::/32", ["2001:db8:4::/48", "2001:db8:fca::/120"], 48, "2001:db8::/48", "2001:db8:ffff::/48", 65534),
|
|
118
134
|
("10.0.0.0/8", ["10.5.0.0/16", "10.255.0.0/24"], 16, "10.0.0.0/16", "10.254.0.0/16", 254),
|
|
119
135
|
("2001:db8::/32", ["2001:db8:4::/48", "2001:db8:ffff::/120"], 48, "2001:db8::/48", "2001:db8:fffe::/48", 65534),
|
|
136
|
+
(
|
|
137
|
+
"10.66.0.0/16",
|
|
138
|
+
["10.66.0.0/24", "10.66.2.64/29", "10.66.4.128/25"],
|
|
139
|
+
24,
|
|
140
|
+
"10.66.1.0/24",
|
|
141
|
+
"10.66.255.0/24",
|
|
142
|
+
253,
|
|
143
|
+
),
|
|
144
|
+
("10.0.0.0/24", [], 32, "10.0.0.0/32", "10.0.0.255/32", 256),
|
|
145
|
+
("10.0.0.0/24", [], 24, "10.0.0.0/24", "10.0.0.0/24", 1),
|
|
120
146
|
],
|
|
121
147
|
)
|
|
122
148
|
def test_free_networks(supernet, used_nets, cidr, first_free, last_free, num_subnets):
|
|
@@ -129,11 +155,11 @@ def test_free_networks(supernet, used_nets, cidr, first_free, last_free, num_sub
|
|
|
129
155
|
|
|
130
156
|
@pytest.mark.parametrize(
|
|
131
157
|
("subnet", "subnet_range"),
|
|
132
|
-
zip(
|
|
158
|
+
list(zip(
|
|
133
159
|
net_attr(v4_subnets, "ipa") + net_attr(v6_subnets, "ipa"),
|
|
134
160
|
net_attr(v4_subnets, "range") + net_attr(v6_subnets, "range"),
|
|
135
161
|
strict=True,
|
|
136
|
-
),
|
|
162
|
+
)),
|
|
137
163
|
)
|
|
138
164
|
def test_netrange(subnet, subnet_range):
|
|
139
165
|
assert tools.netrange(subnet) == subnet_range
|
|
@@ -322,7 +348,7 @@ def test_free_ranges_mismatch(supernet_range, subnet_ranges):
|
|
|
322
348
|
],
|
|
323
349
|
)
|
|
324
350
|
class TestNoFreeRanges:
|
|
325
|
-
def test_no_free_ranges(self, supernet, subnets, free_size):
|
|
351
|
+
def test_no_free_ranges(self, supernet, subnets, free_size): # noqa: ARG002
|
|
326
352
|
range_iterator = tools.free_ranges(supernet.range, net_attr(subnets, "range"))
|
|
327
353
|
with pytest.raises(StopIteration):
|
|
328
354
|
next(range_iterator)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|