Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions tests/test_ipv4_cidr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
import pytest

from validators import ipv4_cidr, ipv6_cidr, ValidationFailure


@pytest.mark.parametrize(('cidr',), [
('127.0.0.1/0',),
('123.5.77.88/8',),
('12.12.12.12/32',),
])
def test_returns_true_on_valid_ipv4_cidr(cidr):
assert ipv4_cidr(cidr)
assert not ipv6_cidr(cidr)


@pytest.mark.parametrize(('cidr',), [
('abc.0.0.1',),
('1.1.1.1',),
('1.1.1.1/-1',),
('1.1.1.1/33',),
('1.1.1.1/foo',),
])
def test_returns_failed_validation_on_invalid_ipv4_cidr(cidr):
assert isinstance(ipv4_cidr(cidr), ValidationFailure)
31 changes: 31 additions & 0 deletions tests/test_ipv6_cidr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
import pytest

from validators import ipv4_cidr, ipv6_cidr, ValidationFailure


@pytest.mark.parametrize(('cidr',), [
('::1/0',),
('dead:beef:0:0:0:0:42:1/8',),
('abcd:ef::42:1/32',),
('0:0:0:0:0:ffff:1.2.3.4/64',),
('::192.168.30.2/128',),
])
def test_returns_true_on_valid_ipv6_cidr(cidr):
assert ipv6_cidr(cidr)
assert not ipv4_cidr(cidr)


@pytest.mark.parametrize(('cidr',), [
('abc.0.0.1',),
('abcd:1234::123::1',),
('1:2:3:4:5:6:7:8:9',),
('abcd::1ffff',),
('1.1.1.1',),
('::1',),
('::1/129',),
('::1/-1',),
('::1/foo',),
])
def test_returns_failed_validation_on_invalid_ipv6_cidr(cidr):
assert isinstance(ipv6_cidr(cidr), ValidationFailure)
6 changes: 3 additions & 3 deletions validators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .hashes import md5, sha1, sha224, sha256, sha512
from .i18n import fi_business_id, fi_ssn
from .iban import iban
from .ip_address import ipv4, ipv6
from .ip_address import ipv4, ipv4_cidr, ipv6, ipv6_cidr
from .length import length
from .mac_address import mac_address
from .slug import slug
Expand All @@ -16,7 +16,7 @@

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

__version__ = '0.13.0'
50 changes: 50 additions & 0 deletions validators/ip_address.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,31 @@ def ipv4(value):
return all(0 <= int(part) < 256 for part in groups)


@validator
def ipv4_cidr(value):
"""
Return whether or not given value is a valid CIDR-notated IP version 4
address range.

This validator is based on RFC4632 3.1.

Examples::

>>> ipv4_cidr('1.1.1.1/8')
True

>>> ipv4_cidr('1.1.1.1')
ValidationFailure(func=ipv4_cidr, args={'value': '1.1.1.1'})
"""
try:
prefix, suffix = value.split('/', 2)
except ValueError:
return False
if not ipv4(prefix) or not suffix.isdigit():
return False
return 0 <= int(suffix) <= 32


@validator
def ipv6(value):
"""
Expand Down Expand Up @@ -92,3 +117,28 @@ def ipv6(value):
elif count_blank == 2 and not ipv6_groups[0] and not ipv6_groups[1]:
return True
return False


@validator
def ipv6_cidr(value):
"""
Returns whether or not given value is a valid CIDR-notated IP version 6
address range.

This validator is based on RFC4632 3.1.

Examples::

>>> ipv6_cidr('::1/128')
True

>>> ipv6_cidr('::1')
ValidationFailure(func=ipv6_cidr, args={'value': '::1'})
"""
try:
prefix, suffix = value.split('/', 2)
except ValueError:
return False
if not ipv6(prefix) or not suffix.isdigit():
return False
return 0 <= int(suffix) <= 128