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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: ipaddons
3
- Version: 0.5.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.5.0'
22
- __version_tuple__ = version_tuple = (0, 5, 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 values unique and sort them. This is crucial when interating over them.
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 = sorted(used_ranges, key=lambda x: (x[0], -x[1]))
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 it.
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 free_range in free_ranges(supernet_range, used_ranges):
171
- free_range_boundary = (
172
- # Large IPv6 integers are tricky...
173
- _ceildiv(free_range[0], cidr_size) * cidr_size,
174
- free_range[1] // cidr_size * cidr_size - 1,
175
- )
176
- for s in range(free_range_boundary[0], free_range_boundary[1] + cidr_size, cidr_size):
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