1
+ from decimal import Decimal
1
2
from re import Pattern
2
- from typing import Any , Optional
3
+ from typing import Any , Optional , Union
3
4
4
5
import pytest
5
6
from pydantic import BaseModel , ValidationError
@@ -34,7 +35,14 @@ class Lng(BaseModel):
34
35
(Coordinate (latitude = 0 , longitude = 0 ), (0 , 0 ), None ),
35
36
(ArgsKwargs (args = ()), (0 , 0 ), None ),
36
37
(ArgsKwargs (args = (1 , 0.0 )), (1.0 , 0 ), None ),
37
- # # Invalid coordinates
38
+ # Decimal test cases
39
+ ((Decimal ('20.0' ), Decimal ('10.0' )), (Decimal ('20.0' ), Decimal ('10.0' )), None ),
40
+ ((Decimal ('-90.0' ), Decimal ('0.0' )), (Decimal ('-90.0' ), Decimal ('0.0' )), None ),
41
+ ((Decimal ('45.678' ), Decimal ('-123.456' )), (Decimal ('45.678' ), Decimal ('-123.456' )), None ),
42
+ (Coordinate (Decimal ('20.0' ), Decimal ('10.0' )), (Decimal ('20.0' ), Decimal ('10.0' )), None ),
43
+ (Coordinate (latitude = Decimal ('0' ), longitude = Decimal ('0' )), (Decimal ('0' ), Decimal ('0' )), None ),
44
+ (ArgsKwargs (args = (Decimal ('1' ), Decimal ('0.0' ))), (Decimal ('1.0' ), Decimal ('0.0' )), None ),
45
+ # Invalid coordinates
38
46
((), None , 'Field required' ), # Empty tuple
39
47
((10.0 ,), None , 'Field required' ), # Tuple with only one value
40
48
(('ten, ' ), None , 'string is not recognized as a valid coordinate' ),
@@ -49,10 +57,11 @@ class Lng(BaseModel):
49
57
(2 , None , 'Input should be a dictionary or an instance of Coordinate' ), # Wrong type
50
58
],
51
59
)
52
- def test_format_for_coordinate (coord : (Any , Any ), result : (float , float ), error : Optional [Pattern ]):
60
+ def test_format_for_coordinate (
61
+ coord : (Any , Any ), result : (Union [float , Decimal ], Union [float , Decimal ]), error : Optional [Pattern ]
62
+ ):
53
63
if error is None :
54
64
_coord : Coordinate = Coord (coord = coord ).coord
55
- print ('vars(_coord)' , vars (_coord ))
56
65
assert _coord .latitude == result [0 ]
57
66
assert _coord .longitude == result [1 ]
58
67
else :
@@ -69,6 +78,16 @@ def test_format_for_coordinate(coord: (Any, Any), result: (float, float), error:
69
78
# Invalid coordinates
70
79
((- 91.0 , 0.0 ), 'Input should be greater than or equal to -90' ),
71
80
((50.0 , 181.0 ), 'Input should be less than or equal to 180' ),
81
+ # Valid Decimal coordinates
82
+ ((Decimal ('-90.0' ), Decimal ('0.0' )), None ),
83
+ ((Decimal ('50.0' ), Decimal ('180.0' )), None ),
84
+ ((Decimal ('-89.999999' ), Decimal ('179.999999' )), None ),
85
+ ((Decimal ('0.0' ), Decimal ('0.0' )), None ),
86
+ # Invalid Decimal coordinates
87
+ ((Decimal ('-90.1' ), Decimal ('0.0' )), 'Input should be greater than or equal to -90' ),
88
+ ((Decimal ('50.0' ), Decimal ('180.1' )), 'Input should be less than or equal to 180' ),
89
+ ((Decimal ('90.1' ), Decimal ('0.0' )), 'Input should be less than or equal to 90' ),
90
+ ((Decimal ('0.0' ), Decimal ('-180.1' )), 'Input should be greater than or equal to -180' ),
72
91
],
73
92
)
74
93
def test_limit_for_coordinate (coord : (Any , Any ), error : Optional [Pattern ]):
@@ -91,17 +110,21 @@ def test_limit_for_coordinate(coord: (Any, Any), error: Optional[Pattern]):
91
110
('90.0' , True ),
92
111
(- 90.0 , True ),
93
112
('-90.0' , True ),
113
+ (Decimal ('90.0' ), True ),
114
+ (Decimal ('-90.0' ), True ),
94
115
# Unvalid latitude
95
116
(91.0 , False ),
96
117
(- 91.0 , False ),
118
+ (Decimal ('91.0' ), False ),
119
+ (Decimal ('-91.0' ), False ),
97
120
],
98
121
)
99
122
def test_format_latitude (latitude : float , valid : bool ):
100
123
if valid :
101
124
_lat = Lat (lat = latitude ).lat
102
125
assert _lat == float (latitude )
103
126
else :
104
- with pytest .raises (ValidationError , match = '1 validation error for Lat' ):
127
+ with pytest .raises (ValidationError , match = '2 validation errors for Lat' ):
105
128
Lat (lat = latitude )
106
129
107
130
@@ -119,46 +142,89 @@ def test_format_latitude(latitude: float, valid: bool):
119
142
(- 91.0 , True ),
120
143
(180.0 , True ),
121
144
(- 180.0 , True ),
145
+ (Decimal ('180.0' ), True ),
146
+ (Decimal ('-180.0' ), True ),
122
147
# Unvalid latitude
123
148
(181.0 , False ),
124
149
(- 181.0 , False ),
150
+ (Decimal ('181.0' ), False ),
151
+ (Decimal ('-181.0' ), False ),
125
152
],
126
153
)
127
154
def test_format_longitude (longitude : float , valid : bool ):
128
155
if valid :
129
156
_lng = Lng (lng = longitude ).lng
130
157
assert _lng == float (longitude )
131
158
else :
132
- with pytest .raises (ValidationError , match = '1 validation error for Lng' ):
159
+ with pytest .raises (ValidationError , match = '2 validation errors for Lng' ):
133
160
Lng (lng = longitude )
134
161
135
162
136
163
def test_str_repr ():
164
+ # Float tests
137
165
assert str (Coord (coord = (20.0 , 10.0 )).coord ) == '20.0,10.0'
138
166
assert str (Coord (coord = ('20.0, 10.0' )).coord ) == '20.0,10.0'
139
167
assert repr (Coord (coord = (20.0 , 10.0 )).coord ) == 'Coordinate(latitude=20.0, longitude=10.0)'
168
+ # Decimal tests
169
+ assert str (Coord (coord = (Decimal ('20.0' ), Decimal ('10.0' ))).coord ) == '20.0,10.0'
170
+ assert str (Coord (coord = (Decimal ('20.000' ), Decimal ('10.000' ))).coord ) == '20.000,10.000'
171
+ assert (
172
+ repr (Coord (coord = (Decimal ('20.0' ), Decimal ('10.0' ))).coord )
173
+ == "Coordinate(latitude=Decimal('20.0'), longitude=Decimal('10.0'))"
174
+ )
140
175
141
176
142
177
def test_eq ():
178
+ # Float tests
143
179
assert Coord (coord = (20.0 , 10.0 )).coord != Coord (coord = '20.0,11.0' ).coord
144
180
assert Coord (coord = ('20.0, 10.0' )).coord != Coord (coord = '20.0,11.0' ).coord
145
181
assert Coord (coord = ('20.0, 10.0' )).coord != Coord (coord = '20.0,11.0' ).coord
146
182
assert Coord (coord = (20.0 , 10.0 )).coord == Coord (coord = '20.0,10.0' ).coord
147
183
184
+ # Decimal tests
185
+ assert Coord (coord = (Decimal ('20.0' ), Decimal ('10.0' ))).coord == Coord (coord = '20.0,10.0' ).coord
186
+ assert Coord (coord = (Decimal ('20.0' ), Decimal ('10.0' ))).coord == Coord (coord = (20.0 , 10.0 )).coord
187
+ assert (
188
+ Coord (coord = (Decimal ('20.0' ), Decimal ('10.0' ))).coord != Coord (coord = (Decimal ('20.0' ), Decimal ('11.0' ))).coord
189
+ )
190
+ assert (
191
+ Coord (coord = (Decimal ('20.000' ), Decimal ('10.000' ))).coord
192
+ == Coord (coord = (Decimal ('20.0' ), Decimal ('10.0' ))).coord
193
+ )
194
+
148
195
149
196
def test_hashable ():
197
+ # Float tests
150
198
assert hash (Coord (coord = (20.0 , 10.0 )).coord ) == hash (Coord (coord = (20.0 , 10.0 )).coord )
151
199
assert hash (Coord (coord = (20.0 , 11.0 )).coord ) != hash (Coord (coord = (20.0 , 10.0 )).coord )
152
200
201
+ # Decimal tests
202
+ assert hash (Coord (coord = (Decimal ('20.0' ), Decimal ('10.0' ))).coord ) == hash (
203
+ Coord (coord = (Decimal ('20.0' ), Decimal ('10.0' ))).coord
204
+ )
205
+ assert hash (Coord (coord = (Decimal ('20.0' ), Decimal ('10.0' ))).coord ) == hash (Coord (coord = (20.0 , 10.0 )).coord )
206
+ assert hash (Coord (coord = (Decimal ('20.0' ), Decimal ('11.0' ))).coord ) != hash (
207
+ Coord (coord = (Decimal ('20.0' ), Decimal ('10.0' ))).coord
208
+ )
209
+ assert hash (Coord (coord = (Decimal ('20.000' ), Decimal ('10.000' ))).coord ) == hash (
210
+ Coord (coord = (Decimal ('20.0' ), Decimal ('10.0' ))).coord
211
+ )
212
+
153
213
154
214
def test_json_schema ():
155
215
class Model (BaseModel ):
156
216
value : Coordinate
157
217
158
218
assert Model .model_json_schema (mode = 'validation' )['$defs' ]['Coordinate' ] == {
159
219
'properties' : {
160
- 'latitude' : {'maximum' : 90.0 , 'minimum' : - 90.0 , 'title' : 'Latitude' , 'type' : 'number' },
161
- 'longitude' : {'maximum' : 180.0 , 'minimum' : - 180.0 , 'title' : 'Longitude' , 'type' : 'number' },
220
+ 'latitude' : {
221
+ 'anyOf' : [{'maximum' : 90.0 , 'minimum' : - 90.0 , 'type' : 'number' }, {'type' : 'string' }],
222
+ 'title' : 'Latitude' ,
223
+ },
224
+ 'longitude' : {
225
+ 'anyOf' : [{'maximum' : 180.0 , 'minimum' : - 180.0 , 'type' : 'number' }, {'type' : 'string' }],
226
+ 'title' : 'Longitude' ,
227
+ },
162
228
},
163
229
'required' : ['latitude' , 'longitude' ],
164
230
'title' : 'Coordinate' ,
@@ -170,7 +236,10 @@ class Model(BaseModel):
170
236
{
171
237
'maxItems' : 2 ,
172
238
'minItems' : 2 ,
173
- 'prefixItems' : [{'type' : 'number' }, {'type' : 'number' }],
239
+ 'prefixItems' : [
240
+ {'anyOf' : [{'type' : 'number' }, {'type' : 'string' }]},
241
+ {'anyOf' : [{'type' : 'number' }, {'type' : 'string' }]},
242
+ ],
174
243
'type' : 'array' ,
175
244
},
176
245
{'type' : 'string' },
@@ -181,8 +250,14 @@ class Model(BaseModel):
181
250
'$defs' : {
182
251
'Coordinate' : {
183
252
'properties' : {
184
- 'latitude' : {'maximum' : 90.0 , 'minimum' : - 90.0 , 'title' : 'Latitude' , 'type' : 'number' },
185
- 'longitude' : {'maximum' : 180.0 , 'minimum' : - 180.0 , 'title' : 'Longitude' , 'type' : 'number' },
253
+ 'latitude' : {
254
+ 'anyOf' : [{'maximum' : 90.0 , 'minimum' : - 90.0 , 'type' : 'number' }, {'type' : 'string' }],
255
+ 'title' : 'Latitude' ,
256
+ },
257
+ 'longitude' : {
258
+ 'anyOf' : [{'maximum' : 180.0 , 'minimum' : - 180.0 , 'type' : 'number' }, {'type' : 'string' }],
259
+ 'title' : 'Longitude' ,
260
+ },
186
261
},
187
262
'required' : ['latitude' , 'longitude' ],
188
263
'title' : 'Coordinate' ,
0 commit comments