mirror of
https://github.com/kennethreitz/pydantic.git
synced 2026-06-05 23:00:18 +00:00
79017111aa
* new URL parsing, fix #603, fix #541 * AnyUrl parts and more tests * more coverage and db DSNs * remove DSN methods * tests for urlstr * remove debug * make AnyStr a subtype of str * fix with cython * rearranging networking code * allowing international domains, cleanup * support international domains * better URL builder * allow underscores in subdomains and domains * tests for json and schema, max length * urlstr > stricturl * updating docs * tweak docs examples * tweak docs
542 lines
17 KiB
Python
542 lines
17 KiB
Python
from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network
|
|
|
|
import pytest
|
|
|
|
from pydantic import BaseModel, IPvAnyAddress, IPvAnyInterface, IPvAnyNetwork, ValidationError
|
|
|
|
#
|
|
# ipaddress.IPv4Address
|
|
# ipaddress.IPv6Address
|
|
# pydantic.IPvAnyAddress
|
|
#
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value,cls',
|
|
[
|
|
('0.0.0.0', IPv4Address),
|
|
('1.1.1.1', IPv4Address),
|
|
('10.10.10.10', IPv4Address),
|
|
('192.168.0.1', IPv4Address),
|
|
('255.255.255.255', IPv4Address),
|
|
('::1:0:1', IPv6Address),
|
|
('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', IPv6Address),
|
|
(b'\x00\x00\x00\x00', IPv4Address),
|
|
(b'\x01\x01\x01\x01', IPv4Address),
|
|
(b'\n\n\n\n', IPv4Address),
|
|
(b'\xc0\xa8\x00\x01', IPv4Address),
|
|
(b'\xff\xff\xff\xff', IPv4Address),
|
|
(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01', IPv6Address),
|
|
(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff', IPv6Address),
|
|
(0, IPv4Address),
|
|
(16_843_009, IPv4Address),
|
|
(168_430_090, IPv4Address),
|
|
(3_232_235_521, IPv4Address),
|
|
(4_294_967_295, IPv4Address),
|
|
(4_294_967_297, IPv6Address),
|
|
(340_282_366_920_938_463_463_374_607_431_768_211_455, IPv6Address),
|
|
(IPv4Address('192.168.0.1'), IPv4Address),
|
|
(IPv6Address('::1:0:1'), IPv6Address),
|
|
],
|
|
)
|
|
def test_ipaddress_success(value, cls):
|
|
class Model(BaseModel):
|
|
ip: IPvAnyAddress
|
|
|
|
assert Model(ip=value).ip == cls(value)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value',
|
|
[
|
|
'0.0.0.0',
|
|
'1.1.1.1',
|
|
'10.10.10.10',
|
|
'192.168.0.1',
|
|
'255.255.255.255',
|
|
b'\x00\x00\x00\x00',
|
|
b'\x01\x01\x01\x01',
|
|
b'\n\n\n\n',
|
|
b'\xc0\xa8\x00\x01',
|
|
b'\xff\xff\xff\xff',
|
|
0,
|
|
16_843_009,
|
|
168_430_090,
|
|
3_232_235_521,
|
|
4_294_967_295,
|
|
IPv4Address('0.0.0.0'),
|
|
IPv4Address('1.1.1.1'),
|
|
IPv4Address('10.10.10.10'),
|
|
IPv4Address('192.168.0.1'),
|
|
IPv4Address('255.255.255.255'),
|
|
],
|
|
)
|
|
def test_ipv4address_success(value):
|
|
class Model(BaseModel):
|
|
ipv4: IPv4Address
|
|
|
|
assert Model(ipv4=value).ipv4 == IPv4Address(value)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value',
|
|
[
|
|
'::1:0:1',
|
|
'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
|
|
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01',
|
|
b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff',
|
|
4_294_967_297,
|
|
340_282_366_920_938_463_463_374_607_431_768_211_455,
|
|
IPv6Address('::1:0:1'),
|
|
IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'),
|
|
],
|
|
)
|
|
def test_ipv6address_success(value):
|
|
class Model(BaseModel):
|
|
ipv6: IPv6Address
|
|
|
|
assert Model(ipv6=value).ipv6 == IPv6Address(value)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value,errors',
|
|
[
|
|
(
|
|
'hello,world',
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv4 or IPv6 address', 'type': 'value_error.ipvanyaddress'}],
|
|
),
|
|
(
|
|
'192.168.0.1.1.1',
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv4 or IPv6 address', 'type': 'value_error.ipvanyaddress'}],
|
|
),
|
|
(
|
|
-1,
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv4 or IPv6 address', 'type': 'value_error.ipvanyaddress'}],
|
|
),
|
|
(
|
|
2 ** 128 + 1,
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv4 or IPv6 address', 'type': 'value_error.ipvanyaddress'}],
|
|
),
|
|
],
|
|
)
|
|
def test_ipaddress_fails(value, errors):
|
|
class Model(BaseModel):
|
|
ip: IPvAnyAddress
|
|
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
Model(ip=value)
|
|
assert exc_info.value.errors() == errors
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value,errors',
|
|
[
|
|
(
|
|
'hello,world',
|
|
[{'loc': ('ipv4',), 'msg': 'value is not a valid IPv4 address', 'type': 'value_error.ipv4address'}],
|
|
),
|
|
(
|
|
'192.168.0.1.1.1',
|
|
[{'loc': ('ipv4',), 'msg': 'value is not a valid IPv4 address', 'type': 'value_error.ipv4address'}],
|
|
),
|
|
(-1, [{'loc': ('ipv4',), 'msg': 'value is not a valid IPv4 address', 'type': 'value_error.ipv4address'}]),
|
|
(
|
|
2 ** 32 + 1,
|
|
[{'loc': ('ipv4',), 'msg': 'value is not a valid IPv4 address', 'type': 'value_error.ipv4address'}],
|
|
),
|
|
(
|
|
IPv6Address('::0:1:0'),
|
|
[{'loc': ('ipv4',), 'msg': 'value is not a valid IPv4 address', 'type': 'value_error.ipv4address'}],
|
|
),
|
|
],
|
|
)
|
|
def test_ipv4address_fails(value, errors):
|
|
class Model(BaseModel):
|
|
ipv4: IPv4Address
|
|
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
Model(ipv4=value)
|
|
assert exc_info.value.errors() == errors
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value,errors',
|
|
[
|
|
(
|
|
'hello,world',
|
|
[{'loc': ('ipv6',), 'msg': 'value is not a valid IPv6 address', 'type': 'value_error.ipv6address'}],
|
|
),
|
|
(
|
|
'192.168.0.1.1.1',
|
|
[{'loc': ('ipv6',), 'msg': 'value is not a valid IPv6 address', 'type': 'value_error.ipv6address'}],
|
|
),
|
|
(-1, [{'loc': ('ipv6',), 'msg': 'value is not a valid IPv6 address', 'type': 'value_error.ipv6address'}]),
|
|
(
|
|
2 ** 128 + 1,
|
|
[{'loc': ('ipv6',), 'msg': 'value is not a valid IPv6 address', 'type': 'value_error.ipv6address'}],
|
|
),
|
|
(
|
|
IPv4Address('192.168.0.1'),
|
|
[{'loc': ('ipv6',), 'msg': 'value is not a valid IPv6 address', 'type': 'value_error.ipv6address'}],
|
|
),
|
|
],
|
|
)
|
|
def test_ipv6address_fails(value, errors):
|
|
class Model(BaseModel):
|
|
ipv6: IPv6Address
|
|
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
Model(ipv6=value)
|
|
assert exc_info.value.errors() == errors
|
|
|
|
|
|
#
|
|
# ipaddress.IPv4Network
|
|
# ipaddress.IPv6Network
|
|
# pydantic.IPvAnyNetwork
|
|
#
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value,cls',
|
|
[
|
|
('192.168.0.0/24', IPv4Network),
|
|
('192.168.128.0/30', IPv4Network),
|
|
('2001:db00::0/120', IPv6Network),
|
|
(2 ** 32 - 1, IPv4Network), # no mask equals to mask /32
|
|
(20_282_409_603_651_670_423_947_251_286_015, IPv6Network), # /128
|
|
(b'\xff\xff\xff\xff', IPv4Network), # /32
|
|
(b'\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff', IPv6Network),
|
|
(('192.168.0.0', 24), IPv4Network),
|
|
(('2001:db00::0', 120), IPv6Network),
|
|
(IPv4Network('192.168.0.0/24'), IPv4Network),
|
|
],
|
|
)
|
|
def test_ipnetwork_success(value, cls):
|
|
class Model(BaseModel):
|
|
ip: IPvAnyNetwork = None
|
|
|
|
assert Model(ip=value).ip == cls(value)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value,cls',
|
|
[
|
|
('192.168.0.0/24', IPv4Network),
|
|
('192.168.128.0/30', IPv4Network),
|
|
(2 ** 32 - 1, IPv4Network), # no mask equals to mask /32
|
|
(b'\xff\xff\xff\xff', IPv4Network), # /32
|
|
(('192.168.0.0', 24), IPv4Network),
|
|
(IPv4Network('192.168.0.0/24'), IPv4Network),
|
|
],
|
|
)
|
|
def test_ip_v4_network_success(value, cls):
|
|
class Model(BaseModel):
|
|
ip: IPv4Network = None
|
|
|
|
assert Model(ip=value).ip == cls(value)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value,cls',
|
|
[
|
|
('2001:db00::0/120', IPv6Network),
|
|
(20_282_409_603_651_670_423_947_251_286_015, IPv6Network), # /128
|
|
(b'\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff', IPv6Network),
|
|
(('2001:db00::0', 120), IPv6Network),
|
|
(IPv6Network('2001:db00::0/120'), IPv6Network),
|
|
],
|
|
)
|
|
def test_ip_v6_network_success(value, cls):
|
|
class Model(BaseModel):
|
|
ip: IPv6Network = None
|
|
|
|
assert Model(ip=value).ip == cls(value)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value,errors',
|
|
[
|
|
(
|
|
'hello,world',
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv4 or IPv6 network', 'type': 'value_error.ipvanynetwork'}],
|
|
),
|
|
(
|
|
'192.168.0.1.1.1/24',
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv4 or IPv6 network', 'type': 'value_error.ipvanynetwork'}],
|
|
),
|
|
(
|
|
-1,
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv4 or IPv6 network', 'type': 'value_error.ipvanynetwork'}],
|
|
),
|
|
(
|
|
2 ** 128 + 1,
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv4 or IPv6 network', 'type': 'value_error.ipvanynetwork'}],
|
|
),
|
|
],
|
|
)
|
|
def test_ipnetwork_fails(value, errors):
|
|
class Model(BaseModel):
|
|
ip: IPvAnyNetwork = None
|
|
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
Model(ip=value)
|
|
assert exc_info.value.errors() == errors
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value,errors',
|
|
[
|
|
(
|
|
'hello,world',
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv4 network', 'type': 'value_error.ipv4network'}],
|
|
),
|
|
(
|
|
'192.168.0.1.1.1/24',
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv4 network', 'type': 'value_error.ipv4network'}],
|
|
),
|
|
(-1, [{'loc': ('ip',), 'msg': 'value is not a valid IPv4 network', 'type': 'value_error.ipv4network'}]),
|
|
(
|
|
2 ** 128 + 1,
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv4 network', 'type': 'value_error.ipv4network'}],
|
|
),
|
|
(
|
|
'2001:db00::1/120',
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv4 network', 'type': 'value_error.ipv4network'}],
|
|
),
|
|
],
|
|
)
|
|
def test_ip_v4_network_fails(value, errors):
|
|
class Model(BaseModel):
|
|
ip: IPv4Network = None
|
|
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
Model(ip=value)
|
|
assert exc_info.value.errors() == errors
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value,errors',
|
|
[
|
|
(
|
|
'hello,world',
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv6 network', 'type': 'value_error.ipv6network'}],
|
|
),
|
|
(
|
|
'192.168.0.1.1.1/24',
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv6 network', 'type': 'value_error.ipv6network'}],
|
|
),
|
|
(-1, [{'loc': ('ip',), 'msg': 'value is not a valid IPv6 network', 'type': 'value_error.ipv6network'}]),
|
|
(
|
|
2 ** 128 + 1,
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv6 network', 'type': 'value_error.ipv6network'}],
|
|
),
|
|
(
|
|
'192.168.0.1/24',
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv6 network', 'type': 'value_error.ipv6network'}],
|
|
),
|
|
],
|
|
)
|
|
def test_ip_v6_network_fails(value, errors):
|
|
class Model(BaseModel):
|
|
ip: IPv6Network = None
|
|
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
Model(ip=value)
|
|
assert exc_info.value.errors() == errors
|
|
|
|
|
|
#
|
|
# ipaddress.IPv4Interface
|
|
# ipaddress.IPv6Interface
|
|
# pydantic.IPvAnyInterface
|
|
#
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value,cls',
|
|
[
|
|
('192.168.0.0/24', IPv4Interface),
|
|
('192.168.0.1/24', IPv4Interface),
|
|
('192.168.128.0/30', IPv4Interface),
|
|
('192.168.128.1/30', IPv4Interface),
|
|
('2001:db00::0/120', IPv6Interface),
|
|
('2001:db00::1/120', IPv6Interface),
|
|
(2 ** 32 - 1, IPv4Interface), # no mask equals to mask /32
|
|
(2 ** 32 - 1, IPv4Interface), # so ``strict`` has no effect
|
|
(20_282_409_603_651_670_423_947_251_286_015, IPv6Interface), # /128
|
|
(20_282_409_603_651_670_423_947_251_286_014, IPv6Interface),
|
|
(b'\xff\xff\xff\xff', IPv4Interface), # /32
|
|
(b'\xff\xff\xff\xff', IPv4Interface),
|
|
(b'\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff', IPv6Interface),
|
|
(b'\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff', IPv6Interface),
|
|
(('192.168.0.0', 24), IPv4Interface),
|
|
(('192.168.0.1', 24), IPv4Interface),
|
|
(('2001:db00::0', 120), IPv6Interface),
|
|
(('2001:db00::1', 120), IPv6Interface),
|
|
(IPv4Interface('192.168.0.0/24'), IPv4Interface),
|
|
(IPv4Interface('192.168.0.1/24'), IPv4Interface),
|
|
(IPv6Interface('2001:db00::0/120'), IPv6Interface),
|
|
(IPv6Interface('2001:db00::1/120'), IPv6Interface),
|
|
],
|
|
)
|
|
def test_ipinterface_success(value, cls):
|
|
class Model(BaseModel):
|
|
ip: IPvAnyInterface = None
|
|
|
|
assert Model(ip=value).ip == cls(value)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value,cls',
|
|
[
|
|
('192.168.0.0/24', IPv4Interface),
|
|
('192.168.0.1/24', IPv4Interface),
|
|
('192.168.128.0/30', IPv4Interface),
|
|
('192.168.128.1/30', IPv4Interface),
|
|
(2 ** 32 - 1, IPv4Interface), # no mask equals to mask /32
|
|
(2 ** 32 - 1, IPv4Interface), # so ``strict`` has no effect
|
|
(b'\xff\xff\xff\xff', IPv4Interface), # /32
|
|
(b'\xff\xff\xff\xff', IPv4Interface),
|
|
(('192.168.0.0', 24), IPv4Interface),
|
|
(('192.168.0.1', 24), IPv4Interface),
|
|
(IPv4Interface('192.168.0.0/24'), IPv4Interface),
|
|
(IPv4Interface('192.168.0.1/24'), IPv4Interface),
|
|
],
|
|
)
|
|
def test_ip_v4_interface_success(value, cls):
|
|
class Model(BaseModel):
|
|
ip: IPv4Interface
|
|
|
|
assert Model(ip=value).ip == cls(value)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value,cls',
|
|
[
|
|
('2001:db00::0/120', IPv6Interface),
|
|
('2001:db00::1/120', IPv6Interface),
|
|
(20_282_409_603_651_670_423_947_251_286_015, IPv6Interface), # /128
|
|
(20_282_409_603_651_670_423_947_251_286_014, IPv6Interface),
|
|
(b'\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff', IPv6Interface),
|
|
(b'\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff', IPv6Interface),
|
|
(('2001:db00::0', 120), IPv6Interface),
|
|
(('2001:db00::1', 120), IPv6Interface),
|
|
(IPv6Interface('2001:db00::0/120'), IPv6Interface),
|
|
(IPv6Interface('2001:db00::1/120'), IPv6Interface),
|
|
],
|
|
)
|
|
def test_ip_v6_interface_success(value, cls):
|
|
class Model(BaseModel):
|
|
ip: IPv6Interface = None
|
|
|
|
assert Model(ip=value).ip == cls(value)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value,errors',
|
|
[
|
|
(
|
|
'hello,world',
|
|
[
|
|
{
|
|
'loc': ('ip',),
|
|
'msg': 'value is not a valid IPv4 or IPv6 interface',
|
|
'type': 'value_error.ipvanyinterface',
|
|
}
|
|
],
|
|
),
|
|
(
|
|
'192.168.0.1.1.1/24',
|
|
[
|
|
{
|
|
'loc': ('ip',),
|
|
'msg': 'value is not a valid IPv4 or IPv6 interface',
|
|
'type': 'value_error.ipvanyinterface',
|
|
}
|
|
],
|
|
),
|
|
(
|
|
-1,
|
|
[
|
|
{
|
|
'loc': ('ip',),
|
|
'msg': 'value is not a valid IPv4 or IPv6 interface',
|
|
'type': 'value_error.ipvanyinterface',
|
|
}
|
|
],
|
|
),
|
|
(
|
|
2 ** 128 + 1,
|
|
[
|
|
{
|
|
'loc': ('ip',),
|
|
'msg': 'value is not a valid IPv4 or IPv6 interface',
|
|
'type': 'value_error.ipvanyinterface',
|
|
}
|
|
],
|
|
),
|
|
],
|
|
)
|
|
def test_ipinterface_fails(value, errors):
|
|
class Model(BaseModel):
|
|
ip: IPvAnyInterface = None
|
|
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
Model(ip=value)
|
|
assert exc_info.value.errors() == errors
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value,errors',
|
|
[
|
|
(
|
|
'hello,world',
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv4 interface', 'type': 'value_error.ipv4interface'}],
|
|
),
|
|
(
|
|
'192.168.0.1.1.1/24',
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv4 interface', 'type': 'value_error.ipv4interface'}],
|
|
),
|
|
(-1, [{'loc': ('ip',), 'msg': 'value is not a valid IPv4 interface', 'type': 'value_error.ipv4interface'}]),
|
|
(
|
|
2 ** 128 + 1,
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv4 interface', 'type': 'value_error.ipv4interface'}],
|
|
),
|
|
],
|
|
)
|
|
def test_ip_v4_interface_fails(value, errors):
|
|
class Model(BaseModel):
|
|
ip: IPv4Interface = None
|
|
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
Model(ip=value)
|
|
assert exc_info.value.errors() == errors
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'value,errors',
|
|
[
|
|
(
|
|
'hello,world',
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv6 interface', 'type': 'value_error.ipv6interface'}],
|
|
),
|
|
(
|
|
'192.168.0.1.1.1/24',
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv6 interface', 'type': 'value_error.ipv6interface'}],
|
|
),
|
|
(-1, [{'loc': ('ip',), 'msg': 'value is not a valid IPv6 interface', 'type': 'value_error.ipv6interface'}]),
|
|
(
|
|
2 ** 128 + 1,
|
|
[{'loc': ('ip',), 'msg': 'value is not a valid IPv6 interface', 'type': 'value_error.ipv6interface'}],
|
|
),
|
|
],
|
|
)
|
|
def test_ip_v6_interface_fails(value, errors):
|
|
class Model(BaseModel):
|
|
ip: IPv6Interface = None
|
|
|
|
with pytest.raises(ValidationError) as exc_info:
|
|
Model(ip=value)
|
|
assert exc_info.value.errors() == errors
|