Skip to content

Commit 6f54393

Browse files
woodruffwkvesteri
authored andcommitted
IPv4 and IPv6 CIDR validators (#125)
* validators: Add IPv4, v6 CIDR validators Closes #117. * tests: Add IPv4, v6 CIDR tests * validators: Update imports * validators: Better split handling, isdigit * tests: Fix tests * validators: Use single quotes
1 parent 477d506 commit 6f54393

File tree

4 files changed

+109
-3
lines changed

4 files changed

+109
-3
lines changed

tests/test_ipv4_cidr.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# -*- coding: utf-8 -*-
2+
import pytest
3+
4+
from validators import ipv4_cidr, ipv6_cidr, ValidationFailure
5+
6+
7+
@pytest.mark.parametrize(('cidr',), [
8+
('127.0.0.1/0',),
9+
('123.5.77.88/8',),
10+
('12.12.12.12/32',),
11+
])
12+
def test_returns_true_on_valid_ipv4_cidr(cidr):
13+
assert ipv4_cidr(cidr)
14+
assert not ipv6_cidr(cidr)
15+
16+
17+
@pytest.mark.parametrize(('cidr',), [
18+
('abc.0.0.1',),
19+
('1.1.1.1',),
20+
('1.1.1.1/-1',),
21+
('1.1.1.1/33',),
22+
('1.1.1.1/foo',),
23+
])
24+
def test_returns_failed_validation_on_invalid_ipv4_cidr(cidr):
25+
assert isinstance(ipv4_cidr(cidr), ValidationFailure)

tests/test_ipv6_cidr.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# -*- coding: utf-8 -*-
2+
import pytest
3+
4+
from validators import ipv4_cidr, ipv6_cidr, ValidationFailure
5+
6+
7+
@pytest.mark.parametrize(('cidr',), [
8+
('::1/0',),
9+
('dead:beef:0:0:0:0:42:1/8',),
10+
('abcd:ef::42:1/32',),
11+
('0:0:0:0:0:ffff:1.2.3.4/64',),
12+
('::192.168.30.2/128',),
13+
])
14+
def test_returns_true_on_valid_ipv6_cidr(cidr):
15+
assert ipv6_cidr(cidr)
16+
assert not ipv4_cidr(cidr)
17+
18+
19+
@pytest.mark.parametrize(('cidr',), [
20+
('abc.0.0.1',),
21+
('abcd:1234::123::1',),
22+
('1:2:3:4:5:6:7:8:9',),
23+
('abcd::1ffff',),
24+
('1.1.1.1',),
25+
('::1',),
26+
('::1/129',),
27+
('::1/-1',),
28+
('::1/foo',),
29+
])
30+
def test_returns_failed_validation_on_invalid_ipv6_cidr(cidr):
31+
assert isinstance(ipv6_cidr(cidr), ValidationFailure)

validators/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from .hashes import md5, sha1, sha224, sha256, sha512
66
from .i18n import fi_business_id, fi_ssn
77
from .iban import iban
8-
from .ip_address import ipv4, ipv6
8+
from .ip_address import ipv4, ipv4_cidr, ipv6, ipv6_cidr
99
from .length import length
1010
from .mac_address import mac_address
1111
from .slug import slug
@@ -16,7 +16,7 @@
1616

1717
__all__ = ('between', 'domain', 'email', 'Max', 'Min', 'md5', 'sha1', 'sha224',
1818
'sha256', 'sha512', 'fi_business_id', 'fi_ssn', 'iban', 'ipv4',
19-
'ipv6', 'length', 'mac_address', 'slug', 'truthy', 'url',
20-
'ValidationFailure', 'validator', 'uuid')
19+
'ipv4_cidr', 'ipv6', 'ipv6_cidr', 'length', 'mac_address', 'slug',
20+
'truthy', 'url', 'ValidationFailure', 'validator', 'uuid')
2121

2222
__version__ = '0.13.0'

validators/ip_address.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,31 @@ def ipv4(value):
2929
return all(0 <= int(part) < 256 for part in groups)
3030

3131

32+
@validator
33+
def ipv4_cidr(value):
34+
"""
35+
Return whether or not given value is a valid CIDR-notated IP version 4
36+
address range.
37+
38+
This validator is based on RFC4632 3.1.
39+
40+
Examples::
41+
42+
>>> ipv4_cidr('1.1.1.1/8')
43+
True
44+
45+
>>> ipv4_cidr('1.1.1.1')
46+
ValidationFailure(func=ipv4_cidr, args={'value': '1.1.1.1'})
47+
"""
48+
try:
49+
prefix, suffix = value.split('/', 2)
50+
except ValueError:
51+
return False
52+
if not ipv4(prefix) or not suffix.isdigit():
53+
return False
54+
return 0 <= int(suffix) <= 32
55+
56+
3257
@validator
3358
def ipv6(value):
3459
"""
@@ -92,3 +117,28 @@ def ipv6(value):
92117
elif count_blank == 2 and not ipv6_groups[0] and not ipv6_groups[1]:
93118
return True
94119
return False
120+
121+
122+
@validator
123+
def ipv6_cidr(value):
124+
"""
125+
Returns whether or not given value is a valid CIDR-notated IP version 6
126+
address range.
127+
128+
This validator is based on RFC4632 3.1.
129+
130+
Examples::
131+
132+
>>> ipv6_cidr('::1/128')
133+
True
134+
135+
>>> ipv6_cidr('::1')
136+
ValidationFailure(func=ipv6_cidr, args={'value': '::1'})
137+
"""
138+
try:
139+
prefix, suffix = value.split('/', 2)
140+
except ValueError:
141+
return False
142+
if not ipv6(prefix) or not suffix.isdigit():
143+
return False
144+
return 0 <= int(suffix) <= 128

0 commit comments

Comments
 (0)