mirror of
https://github.com/kennethreitz/pydantic.git
synced 2026-06-05 23:00:18 +00:00
IPv{4,6,Any}{Interface,Network} types added (#419)
* IPv{4,6,Any}{Network,Interface} types added, unittests and docs updated
* HISTORY.rst minor update
* Remove strict argument from IP network types
* IP Networks validators and type hints fixed
* tweak history
This commit is contained in:
committed by
Samuel Colvin
parent
1fd509d8a4
commit
0458f9ece9
+6
-1
@@ -3,10 +3,15 @@
|
||||
History
|
||||
-------
|
||||
|
||||
v0.21.1 (unreleased)
|
||||
....................
|
||||
* add ``IPv{4,6,Any}Network`` and ``IPv{4,6,Any}Interface`` types from ``ipaddress`` stdlib, #333 by @pilosus
|
||||
|
||||
|
||||
v0.21.0 (2019-03-15)
|
||||
....................
|
||||
* fix typo in ``NoneIsNotAllowedError`` message, #414 by @YaraslauZhylko
|
||||
* add ``IPAddress``, ``IPv4Address`` and ``IPv6Address`` types, #333 by @pilosus
|
||||
* add ``IPvAnyAddress``, ``IPv4Address`` and ``IPv6Address`` types, #333 by @pilosus
|
||||
|
||||
v0.20.1 (2019-02-26)
|
||||
....................
|
||||
|
||||
+24
-6
@@ -1,12 +1,12 @@
|
||||
import uuid
|
||||
from decimal import Decimal
|
||||
from ipaddress import IPv4Address, IPv6Address
|
||||
from ipaddress import IPv4Address, IPv6Address, IPv4Interface, IPv6Interface, IPv4Network, IPv6Network
|
||||
from pathlib import Path
|
||||
from uuid import UUID
|
||||
|
||||
from pydantic import (DSN, UUID1, UUID3, UUID4, UUID5, BaseModel, DirectoryPath, EmailStr, FilePath, NameEmail,
|
||||
NegativeFloat, NegativeInt, PositiveFloat, PositiveInt, PyObject, UrlStr, conbytes, condecimal,
|
||||
confloat, conint, constr, IPvAnyAddress)
|
||||
confloat, conint, constr, IPvAnyAddress, IPvAnyInterface, IPvAnyNetwork)
|
||||
|
||||
|
||||
class Model(BaseModel):
|
||||
@@ -60,6 +60,12 @@ class Model(BaseModel):
|
||||
ipvany: IPvAnyAddress = None
|
||||
ipv4: IPv4Address = None
|
||||
ipv6: IPv6Address = None
|
||||
ip_vany_network: IPvAnyNetwork = None
|
||||
ip_v4_network: IPv4Network = None
|
||||
ip_v6_network: IPv6Network = None
|
||||
ip_vany_interface: IPvAnyInterface = None
|
||||
ip_v4_interface: IPv4Interface = None
|
||||
ip_v6_interface: IPv6Interface = None
|
||||
|
||||
m = Model(
|
||||
cos_function='math.cos',
|
||||
@@ -95,7 +101,13 @@ m = Model(
|
||||
uuid_v5=uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org'),
|
||||
ipvany=IPv4Address('192.168.0.1'),
|
||||
ipv4=IPv4Address('255.255.255.255'),
|
||||
ipv6=IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')
|
||||
ipv6=IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'),
|
||||
ip_vany_network=IPv4Network('192.168.0.0/24'),
|
||||
ip_v4_network=IPv4Network('192.168.0.0/24'),
|
||||
ip_v6_network=IPv6Network('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128'),
|
||||
ip_vany_interface=IPv4Interface('192.168.0.0/24'),
|
||||
ip_v4_interface=IPv4Interface('192.168.0.0/24'),
|
||||
ip_v6_interface=IPv6Interface('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128')
|
||||
)
|
||||
print(m.dict())
|
||||
"""
|
||||
@@ -133,8 +145,14 @@ print(m.dict())
|
||||
'uuid_v3': UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e'),
|
||||
'uuid_v4': UUID('22209f7a-aad1-491c-bb83-ea19b906d210'),
|
||||
'uuid_v5': UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d'),
|
||||
'ip'='192.168.0.1',
|
||||
'ipv4'='255.255.255.255',
|
||||
'ipv6'='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
|
||||
'ipvany': IPv4Address('192.168.0.1'),
|
||||
'ipv4': IPv4Address('255.255.255.255'),
|
||||
'ipv6': IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'),
|
||||
'ip_vany_network': IPv4Network('192.168.0.0/24'),
|
||||
'ip_v4_network': IPv4Network('192.168.0.0/24'),
|
||||
'ip_v6_network': IPv4Network('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128'),
|
||||
'ip_vany_interface': IPv4Interface('192.168.0.0/24'),
|
||||
'ip_v4_interface': IPv4Interface('192.168.0.0/24'),
|
||||
'ip_v6_interface': IPv6Interface('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128')
|
||||
}
|
||||
"""
|
||||
|
||||
@@ -247,6 +247,20 @@ table = [
|
||||
'Pydantic standard "format" extension',
|
||||
'IPv4 or IPv4 address as used in ``ipaddress`` module',
|
||||
],
|
||||
[
|
||||
'IPvAnyInterface',
|
||||
'string',
|
||||
'{"format": "ipvanyinterface"}',
|
||||
'Pydantic standard "format" extension',
|
||||
'IPv4 or IPv4 interface as used in ``ipaddress`` module',
|
||||
],
|
||||
[
|
||||
'IPvAnyNetwork',
|
||||
'string',
|
||||
'{"format": "ipvanynetwork"}',
|
||||
'Pydantic standard "format" extension',
|
||||
'IPv4 or IPv4 network as used in ``ipaddress`` module',
|
||||
],
|
||||
[
|
||||
'StrictStr',
|
||||
'string',
|
||||
|
||||
@@ -294,9 +294,33 @@ class IPvAnyAddressError(PydanticValueError):
|
||||
msg_template = 'value is not a valid IPv4 or IPv6 address'
|
||||
|
||||
|
||||
class IPvAnyInterfaceError(PydanticValueError):
|
||||
msg_template = 'value is not a valid IPv4 or IPv6 interface'
|
||||
|
||||
|
||||
class IPvAnyNetworkError(PydanticValueError):
|
||||
msg_template = 'value is not a valid IPv4 or IPv6 network'
|
||||
|
||||
|
||||
class IPv4AddressError(PydanticValueError):
|
||||
msg_template = 'value is not a valid IPv4 address'
|
||||
|
||||
|
||||
class IPv6AddressError(PydanticValueError):
|
||||
msg_template = 'value is not a valid IPv6 address'
|
||||
|
||||
|
||||
class IPv4NetworkError(PydanticValueError):
|
||||
msg_template = 'value is not a valid IPv4 network'
|
||||
|
||||
|
||||
class IPv6NetworkError(PydanticValueError):
|
||||
msg_template = 'value is not a valid IPv6 network'
|
||||
|
||||
|
||||
class IPv4InterfaceError(PydanticValueError):
|
||||
msg_template = 'value is not a valid IPv4 interface'
|
||||
|
||||
|
||||
class IPv6InterfaceError(PydanticValueError):
|
||||
msg_template = 'value is not a valid IPv6 interface'
|
||||
|
||||
+48
-2
@@ -1,9 +1,18 @@
|
||||
import json
|
||||
import re
|
||||
from decimal import Decimal
|
||||
from ipaddress import IPv4Address, IPv6Address, _BaseAddress
|
||||
from ipaddress import (
|
||||
IPv4Address,
|
||||
IPv4Interface,
|
||||
IPv4Network,
|
||||
IPv6Address,
|
||||
IPv6Interface,
|
||||
IPv6Network,
|
||||
_BaseAddress,
|
||||
_BaseNetwork,
|
||||
)
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Dict, Generator, Optional, Pattern, Set, Type, Union, cast
|
||||
from typing import TYPE_CHECKING, Any, Dict, Generator, Optional, Pattern, Set, Tuple, Type, Union, cast
|
||||
from uuid import UUID
|
||||
|
||||
from . import errors
|
||||
@@ -63,6 +72,8 @@ __all__ = [
|
||||
'Json',
|
||||
'JsonWrapper',
|
||||
'IPvAnyAddress',
|
||||
'IPvAnyInterface',
|
||||
'IPvAnyNetwork',
|
||||
]
|
||||
|
||||
NoneStr = Optional[str]
|
||||
@@ -72,6 +83,7 @@ NoneStrBytes = Optional[StrBytes]
|
||||
OptionalInt = Optional[int]
|
||||
OptionalIntFloat = Union[OptionalInt, float]
|
||||
OptionalIntFloatDecimal = Union[OptionalIntFloat, Decimal]
|
||||
NetworkType = Union[str, bytes, int, Tuple[Union[str, bytes, int], Union[str, int]]]
|
||||
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
@@ -516,3 +528,37 @@ class IPvAnyAddress(_BaseAddress):
|
||||
|
||||
with change_exception(errors.IPvAnyAddressError, ValueError):
|
||||
return IPv6Address(value)
|
||||
|
||||
|
||||
class IPvAnyInterface(_BaseAddress):
|
||||
@classmethod
|
||||
def __get_validators__(cls) -> 'CallableGenerator':
|
||||
yield cls.validate
|
||||
|
||||
@classmethod
|
||||
def validate(cls, value: NetworkType) -> Union[IPv4Interface, IPv6Interface]:
|
||||
try:
|
||||
return IPv4Interface(value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
with change_exception(errors.IPvAnyInterfaceError, ValueError):
|
||||
return IPv6Interface(value)
|
||||
|
||||
|
||||
class IPvAnyNetwork(_BaseNetwork): # type: ignore
|
||||
@classmethod
|
||||
def __get_validators__(cls) -> 'CallableGenerator':
|
||||
yield cls.validate
|
||||
|
||||
@classmethod
|
||||
def validate(cls, value: NetworkType) -> Union[IPv4Network, IPv6Network]:
|
||||
# Assume IP Network is defined with a default value for ``strict`` argument.
|
||||
# Define your own class if you want to specify network address check strictness.
|
||||
try:
|
||||
return IPv4Network(value)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
with change_exception(errors.IPvAnyNetworkError, ValueError):
|
||||
return IPv6Network(value)
|
||||
|
||||
+51
-2
@@ -3,7 +3,7 @@ from collections import OrderedDict
|
||||
from datetime import date, datetime, time, timedelta
|
||||
from decimal import Decimal, DecimalException
|
||||
from enum import Enum
|
||||
from ipaddress import IPv4Address, IPv6Address
|
||||
from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Pattern, Set, Tuple, Type, TypeVar, Union, cast
|
||||
from uuid import UUID
|
||||
@@ -235,6 +235,50 @@ def ip_v6_address_validator(v: Any) -> IPv6Address:
|
||||
return IPv6Address(v)
|
||||
|
||||
|
||||
def ip_v4_network_validator(v: Any) -> IPv4Network:
|
||||
"""
|
||||
Assume IPv4Network initialised with a default ``strict`` argument
|
||||
|
||||
See more:
|
||||
https://docs.python.org/library/ipaddress.html#ipaddress.IPv4Network
|
||||
"""
|
||||
if isinstance(v, IPv4Network):
|
||||
return v
|
||||
|
||||
with change_exception(errors.IPv4NetworkError, ValueError):
|
||||
return IPv4Network(v)
|
||||
|
||||
|
||||
def ip_v6_network_validator(v: Any) -> IPv6Network:
|
||||
"""
|
||||
Assume IPv6Network initialised with a default ``strict`` argument
|
||||
|
||||
See more:
|
||||
https://docs.python.org/library/ipaddress.html#ipaddress.IPv6Network
|
||||
"""
|
||||
if isinstance(v, IPv6Network):
|
||||
return v
|
||||
|
||||
with change_exception(errors.IPv6NetworkError, ValueError):
|
||||
return IPv6Network(v)
|
||||
|
||||
|
||||
def ip_v4_interface_validator(v: Any) -> IPv4Interface:
|
||||
if isinstance(v, IPv4Interface):
|
||||
return v
|
||||
|
||||
with change_exception(errors.IPv4InterfaceError, ValueError):
|
||||
return IPv4Interface(v)
|
||||
|
||||
|
||||
def ip_v6_interface_validator(v: Any) -> IPv6Interface:
|
||||
if isinstance(v, IPv6Interface):
|
||||
return v
|
||||
|
||||
with change_exception(errors.IPv6InterfaceError, ValueError):
|
||||
return IPv6Interface(v)
|
||||
|
||||
|
||||
def path_validator(v: Any) -> Path:
|
||||
if isinstance(v, Path):
|
||||
return v
|
||||
@@ -280,7 +324,8 @@ def pattern_validator(v: Any) -> Pattern[str]:
|
||||
|
||||
|
||||
pattern_validators = [not_none_validator, str_validator, pattern_validator]
|
||||
# order is important here, for example: bool is a subclass of int so has to come first, datetime before date same
|
||||
# order is important here, for example: bool is a subclass of int so has to come first, datetime before date same,
|
||||
# IPv4Interface before IPv4Address, etc
|
||||
_VALIDATORS: List[Tuple[AnyType, List[AnyCallable]]] = [
|
||||
(Enum, [enum_validator]),
|
||||
(str, [not_none_validator, str_validator, anystr_strip_whitespace, anystr_length_validator]),
|
||||
@@ -301,8 +346,12 @@ _VALIDATORS: List[Tuple[AnyType, List[AnyCallable]]] = [
|
||||
(set, [set_validator]),
|
||||
(UUID, [not_none_validator, uuid_validator]),
|
||||
(Decimal, [not_none_validator, decimal_validator]),
|
||||
(IPv4Interface, [not_none_validator, ip_v4_interface_validator]),
|
||||
(IPv6Interface, [not_none_validator, ip_v6_interface_validator]),
|
||||
(IPv4Address, [not_none_validator, ip_v4_address_validator]),
|
||||
(IPv6Address, [not_none_validator, ip_v6_address_validator]),
|
||||
(IPv4Network, [not_none_validator, ip_v4_network_validator]),
|
||||
(IPv6Network, [not_none_validator, ip_v6_network_validator]),
|
||||
]
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@ from distutils.version import StrictVersion
|
||||
|
||||
__all__ = ['VERSION']
|
||||
|
||||
VERSION = StrictVersion('0.21')
|
||||
VERSION = StrictVersion('0.21.1')
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
from ipaddress import IPv4Address, IPv6Address
|
||||
from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network
|
||||
|
||||
import pytest
|
||||
|
||||
from pydantic import BaseModel, IPvAnyAddress, ValidationError
|
||||
from pydantic import BaseModel, IPvAnyAddress, IPvAnyInterface, IPvAnyNetwork, ValidationError
|
||||
|
||||
#
|
||||
# ipaddress.IPv4Address
|
||||
# ipaddress.IPv6Address
|
||||
# pydantic.IPvAnyAddress
|
||||
#
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -182,3 +188,354 @@ def test_ipv6address_fails(value, errors):
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user