Skip to content

Commit bad804b

Browse files
ENH: Added ip_range (#27)
1 parent 79c4561 commit bad804b

File tree

4 files changed

+109
-5
lines changed

4 files changed

+109
-5
lines changed

cyberpandas/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
IPArray,
66
IPAccessor,
77
)
8+
from .ip_methods import ip_range
89
from .parser import to_ipaddress
910
from .mac_array import MACType, MACArray
1011

@@ -26,5 +27,6 @@
2627
'IPType',
2728
'MACArray',
2829
'MACType',
30+
'ip_range',
2931
'to_ipaddress',
3032
]

cyberpandas/ip_methods.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import ipaddress
2+
3+
import numpy as np
4+
import six
5+
6+
from .ip_array import IPArray
7+
from .common import _U8_MAX
8+
9+
10+
def _as_int(ip):
11+
# type: (Union[int, str, IPv4Address, IPv6Address]) -> int
12+
if isinstance(ip, six.string_types):
13+
ip = ipaddress.ip_address(ip)
14+
return int(ip)
15+
16+
17+
def _crosses_boundary(lo, hi):
18+
return (lo <= _U8_MAX) == (hi <= _U8_MAX)
19+
20+
21+
def ip_range(start=None, stop=None, step=None):
22+
"""Generate a range of IP Addresses
23+
24+
Parameters
25+
----------
26+
start : int, str, IPv4Address, or IPv6Address, optional
27+
Start of interval. The interval includes this value. The default
28+
start value is 0.
29+
start : int, str, IPv4Address, or IPv6Address, optional
30+
End of interval. The interval does not include this value.
31+
step : int, optional
32+
Spacing between values. For any output `out`, this is the distance
33+
between two adjacent values, ``out[i+1] - out[i]``. The default
34+
step size is 1. If `step` is specified as a position argument,
35+
`start` must also be given.
36+
37+
Returns
38+
-------
39+
IPArray
40+
41+
Notes
42+
-----
43+
Performance will worsen if either of `start` or `stop` are larger than
44+
2**64.
45+
46+
Examples
47+
--------
48+
From integers
49+
50+
>>> ip_range(1, 5)
51+
IPArray(['0.0.0.1', '0.0.0.2', '0.0.0.3', '0.0.0.4'])
52+
53+
Or strings
54+
55+
>>> ip_range('0.0.0.1', '0.0.0.5')
56+
IPArray(['0.0.0.1', '0.0.0.2', '0.0.0.3', '0.0.0.4'])
57+
58+
Or `ipaddress` objects
59+
60+
>>> ip_range(ipaddress.IPv4Address(1), ipaddress.IPv4Address(5))
61+
IPArray(['0.0.0.1', '0.0.0.2', '0.0.0.3', '0.0.0.4'])
62+
"""
63+
if start is not None:
64+
start = _as_int(start)
65+
if stop is not None:
66+
stop = _as_int(stop)
67+
if step is not None:
68+
step = _as_int(step)
69+
arr = IPArray(np.arange(start, stop, step))
70+
return arr

cyberpandas/parser.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,17 @@ def _to_ip_array(values):
4747

4848
if isinstance(values, IPArray):
4949
return values.data
50-
if not (isinstance(values, np.ndarray) and
51-
values.dtype == IPType._record_type):
50+
51+
if (isinstance(values, np.ndarray) and
52+
values.ndim == 1 and
53+
np.issubdtype(values.dtype, np.integer)):
54+
# We assume we're given the low bits here.
55+
values = values.astype("u8")
56+
values = np.asarray(values, dtype=IPType._record_type)
57+
values['hi'] = 0
58+
59+
elif not (isinstance(values, np.ndarray) and
60+
values.dtype == IPType._record_type):
5261
values = _to_int_pairs(values)
5362
return np.atleast_1d(np.asarray(values, dtype=IPType._record_type))
5463

@@ -61,9 +70,6 @@ def _to_int_pairs(values):
6170
if values.ndim != 2:
6271
raise ValueError("'values' should be a 2-D when passing a "
6372
"NumPy array.")
64-
if values.dtype != int:
65-
raise ValueError("'values' should be integer dtype when "
66-
"passing a NumPy array.")
6773
elif isinstance(values, tuple) and len(values) == 2:
6874
# like IPArray((0, 0))
6975
# which isn't IPArray([0, 0])

tests/ip/test_ip.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,3 +301,29 @@ def test_factorize():
301301
uniques = uniques.astype(object)
302302
tm.assert_numpy_array_equal(labels, expected_labels)
303303
tm.assert_numpy_array_equal(uniques, expected_uniques)
304+
305+
306+
@pytest.mark.parametrize('values', [
307+
[0, 1, 2],
308+
])
309+
def test_from_ndarray(values):
310+
result = ip.IPArray(np.asarray(values))
311+
expected = ip.IPArray(values)
312+
assert result.equals(expected)
313+
314+
315+
@pytest.mark.parametrize('start, stop, step, expected', [
316+
(1, 3, None, [1, 2]),
317+
('0.0.0.1', '0.0.0.3', None, [1, 2]),
318+
(2**64 + 1, 2**64 + 3, None, [2**64 + 1, 2**64 + 2]),
319+
('::1:0:0:0:1', '::1:0:0:0:3', None, [2**64 + 1, 2**64 + 2]),
320+
(2**64 - 1, 2**64 + 2, None, [2**64 - 1, 2**64, 2**64 + 1]),
321+
('::ffff:ffff:ffff:ffff', '::1:0:0:0:2', None,
322+
[2**64 - 1, 2**64, 2**64 + 1]),
323+
(1, 6, 2, [1, 3, 5]),
324+
('0.0.0.1', '0.0.0.6', '0.0.0.2', [1, 3, 5]),
325+
])
326+
def test_ip_range(start, stop, step, expected):
327+
result = ip.ip_range(start, stop, step)
328+
expected = ip.IPArray(expected)
329+
assert result.equals(expected)

0 commit comments

Comments
 (0)