File: test_complex.py

package info (click to toggle)
pydantic-core 2.37.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,784 kB
  • sloc: python: 34,800; javascript: 211; makefile: 126
file content (156 lines) | stat: -rw-r--r-- 6,164 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import math
import platform
import re
import sys

import pytest

from pydantic_core import SchemaValidator, ValidationError
from pydantic_core import core_schema as cs

from ..conftest import Err

EXPECTED_PARSE_ERROR_MESSAGE = 'Input should be a valid complex string following the rules at https://docs.python.org/3/library/functions.html#complex'
EXPECTED_TYPE_ERROR_MESSAGE = 'Input should be a valid python complex object, a number, or a valid complex string following the rules at https://docs.python.org/3/library/functions.html#complex'
EXPECTED_TYPE_ERROR_PY_STRICT_MESSAGE = 'Input should be an instance of complex'


@pytest.mark.parametrize(
    'input_value,expected',
    [
        (complex(2, 4), complex(2, 4)),
        ('2', complex(2, 0)),
        ('2j', complex(0, 2)),
        ('+1.23e-4-5.67e+8J', complex(1.23e-4, -5.67e8)),
        ('1.5-j', complex(1.5, -1)),
        ('-j', complex(0, -1)),
        ('j', complex(0, 1)),
        (3, complex(3, 0)),
        (2.0, complex(2, 0)),
        ('1e-700j', complex(0, 0)),
        ('', Err(EXPECTED_TYPE_ERROR_MESSAGE)),
        ('\t( -1.23+4.5J   \n', Err(EXPECTED_TYPE_ERROR_MESSAGE)),
        ({'real': 2, 'imag': 4}, Err(EXPECTED_TYPE_ERROR_MESSAGE)),
        ({'real': 'test', 'imag': 1}, Err(EXPECTED_TYPE_ERROR_MESSAGE)),
        ({'real': True, 'imag': 1}, Err(EXPECTED_TYPE_ERROR_MESSAGE)),
        ('foobar', Err(EXPECTED_TYPE_ERROR_MESSAGE)),
        ([], Err(EXPECTED_TYPE_ERROR_MESSAGE)),
        ([('x', 'y')], Err(EXPECTED_TYPE_ERROR_MESSAGE)),
        ((), Err(EXPECTED_TYPE_ERROR_MESSAGE)),
        ((('x', 'y'),), Err(EXPECTED_TYPE_ERROR_MESSAGE)),
        (
            (type('Foobar', (), {'x': 1})()),
            Err(EXPECTED_TYPE_ERROR_MESSAGE),
        ),
    ],
    ids=repr,
)
def test_complex_cases(input_value, expected):
    v = SchemaValidator(cs.complex_schema())
    if isinstance(expected, Err):
        with pytest.raises(ValidationError, match=re.escape(expected.message)):
            v.validate_python(input_value)
    else:
        assert v.validate_python(input_value) == expected


@pytest.mark.parametrize(
    'input_value,expected',
    [
        (complex(2, 4), complex(2, 4)),
        ('2', Err(EXPECTED_TYPE_ERROR_PY_STRICT_MESSAGE)),
        ('2j', Err(EXPECTED_TYPE_ERROR_PY_STRICT_MESSAGE)),
        ('+1.23e-4-5.67e+8J', Err(EXPECTED_TYPE_ERROR_PY_STRICT_MESSAGE)),
        ('1.5-j', Err(EXPECTED_TYPE_ERROR_PY_STRICT_MESSAGE)),
        ('-j', Err(EXPECTED_TYPE_ERROR_PY_STRICT_MESSAGE)),
        ('j', Err(EXPECTED_TYPE_ERROR_PY_STRICT_MESSAGE)),
        (3, Err(EXPECTED_TYPE_ERROR_PY_STRICT_MESSAGE)),
        (2.0, Err(EXPECTED_TYPE_ERROR_PY_STRICT_MESSAGE)),
        ('1e-700j', Err(EXPECTED_TYPE_ERROR_PY_STRICT_MESSAGE)),
        ('', Err(EXPECTED_TYPE_ERROR_PY_STRICT_MESSAGE)),
        ('\t( -1.23+4.5J   \n', Err(EXPECTED_TYPE_ERROR_PY_STRICT_MESSAGE)),
        ({'real': 2, 'imag': 4}, Err(EXPECTED_TYPE_ERROR_PY_STRICT_MESSAGE)),
        ({'real': 'test', 'imag': 1}, Err(EXPECTED_TYPE_ERROR_PY_STRICT_MESSAGE)),
        ({'real': True, 'imag': 1}, Err(EXPECTED_TYPE_ERROR_PY_STRICT_MESSAGE)),
        ('foobar', Err(EXPECTED_TYPE_ERROR_PY_STRICT_MESSAGE)),
    ],
    ids=repr,
)
def test_complex_strict(input_value, expected):
    v = SchemaValidator(cs.complex_schema(strict=True))
    if isinstance(expected, Err):
        with pytest.raises(ValidationError, match=re.escape(expected.message)):
            v.validate_python(input_value)
    else:
        assert v.validate_python(input_value) == expected


@pytest.mark.xfail(
    platform.python_implementation() == 'PyPy' and sys.pypy_version_info < (7, 3, 17),
    reason='PyPy cannot process this string due to a bug, even if this string is considered valid in python',
)
def test_valid_complex_string_with_space():
    v = SchemaValidator(cs.complex_schema())
    assert v.validate_python('\t( -1.23+4.5J )\n') == complex(-1.23, 4.5)


def test_nan_inf_complex():
    v = SchemaValidator(cs.complex_schema())
    c = v.validate_python('NaN+Infinityj')
    # c != complex(float('nan'), float('inf')) as nan != nan,
    # so we need to examine the values individually
    assert math.isnan(c.real)
    assert math.isinf(c.imag)


def test_overflow_complex():
    # Python simply converts too large float values to inf, so these strings
    # are still valid, even if the numbers are out of range
    v = SchemaValidator(cs.complex_schema())

    c = v.validate_python('5e600j')
    assert math.isinf(c.imag)

    c = v.validate_python('-5e600j')
    assert math.isinf(c.imag)


def test_json_complex():
    v = SchemaValidator(cs.complex_schema())
    assert v.validate_json('"-1.23e+4+5.67e-8J"') == complex(-1.23e4, 5.67e-8)
    assert v.validate_json('1') == complex(1, 0)
    assert v.validate_json('1.0') == complex(1, 0)
    # "1" is a valid complex string
    assert v.validate_json('"1"') == complex(1, 0)

    with pytest.raises(ValidationError) as exc_info:
        v.validate_json('{"real": 2, "imag": 4}')
    assert exc_info.value.errors(include_url=False) == [
        {
            'type': 'complex_type',
            'loc': (),
            'msg': EXPECTED_TYPE_ERROR_MESSAGE,
            'input': {'real': 2, 'imag': 4},
        }
    ]


def test_json_complex_strict():
    v = SchemaValidator(cs.complex_schema(strict=True))
    assert v.validate_json('"-1.23e+4+5.67e-8J"') == complex(-1.23e4, 5.67e-8)
    # "1" is a valid complex string
    assert v.validate_json('"1"') == complex(1, 0)

    with pytest.raises(ValidationError, match=re.escape(EXPECTED_PARSE_ERROR_MESSAGE)):
        v.validate_json('1')
    with pytest.raises(ValidationError, match=re.escape(EXPECTED_PARSE_ERROR_MESSAGE)):
        v.validate_json('1.0')
    with pytest.raises(ValidationError, match=re.escape(EXPECTED_TYPE_ERROR_MESSAGE)):
        v.validate_json('{"real": 2, "imag": 4}')


def test_string_complex():
    v = SchemaValidator(cs.complex_schema())
    assert v.validate_strings('+1.23e-4-5.67e+8J') == complex(1.23e-4, -5.67e8)
    with pytest.raises(ValidationError, match=re.escape(EXPECTED_PARSE_ERROR_MESSAGE)):
        v.validate_strings("{'real': 1, 'imag': 0}")