1+ from decimal import Decimal
12from re import Pattern
2- from typing import Any , Optional
3+ from typing import Any , Optional , Union
34
45import pytest
56from pydantic import BaseModel , ValidationError
@@ -34,7 +35,14 @@ class Lng(BaseModel):
3435 (Coordinate (latitude = 0 , longitude = 0 ), (0 , 0 ), None ),
3536 (ArgsKwargs (args = ()), (0 , 0 ), None ),
3637 (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
3846 ((), None , 'Field required' ), # Empty tuple
3947 ((10.0 ,), None , 'Field required' ), # Tuple with only one value
4048 (('ten, ' ), None , 'string is not recognized as a valid coordinate' ),
@@ -49,10 +57,11 @@ class Lng(BaseModel):
4957 (2 , None , 'Input should be a dictionary or an instance of Coordinate' ), # Wrong type
5058 ],
5159)
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+ ):
5363 if error is None :
5464 _coord : Coordinate = Coord (coord = coord ).coord
55- print ('vars(_coord)' , vars (_coord ))
5665 assert _coord .latitude == result [0 ]
5766 assert _coord .longitude == result [1 ]
5867 else :
@@ -69,6 +78,16 @@ def test_format_for_coordinate(coord: (Any, Any), result: (float, float), error:
6978 # Invalid coordinates
7079 ((- 91.0 , 0.0 ), 'Input should be greater than or equal to -90' ),
7180 ((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' ),
7291 ],
7392)
7493def 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]):
91110 ('90.0' , True ),
92111 (- 90.0 , True ),
93112 ('-90.0' , True ),
113+ (Decimal ('90.0' ), True ),
114+ (Decimal ('-90.0' ), True ),
94115 # Unvalid latitude
95116 (91.0 , False ),
96117 (- 91.0 , False ),
118+ (Decimal ('91.0' ), False ),
119+ (Decimal ('-91.0' ), False ),
97120 ],
98121)
99122def test_format_latitude (latitude : float , valid : bool ):
100123 if valid :
101124 _lat = Lat (lat = latitude ).lat
102125 assert _lat == float (latitude )
103126 else :
104- with pytest .raises (ValidationError , match = '1 validation error for Lat' ):
127+ with pytest .raises (ValidationError , match = '2 validation errors for Lat' ):
105128 Lat (lat = latitude )
106129
107130
@@ -119,46 +142,89 @@ def test_format_latitude(latitude: float, valid: bool):
119142 (- 91.0 , True ),
120143 (180.0 , True ),
121144 (- 180.0 , True ),
145+ (Decimal ('180.0' ), True ),
146+ (Decimal ('-180.0' ), True ),
122147 # Unvalid latitude
123148 (181.0 , False ),
124149 (- 181.0 , False ),
150+ (Decimal ('181.0' ), False ),
151+ (Decimal ('-181.0' ), False ),
125152 ],
126153)
127154def test_format_longitude (longitude : float , valid : bool ):
128155 if valid :
129156 _lng = Lng (lng = longitude ).lng
130157 assert _lng == float (longitude )
131158 else :
132- with pytest .raises (ValidationError , match = '1 validation error for Lng' ):
159+ with pytest .raises (ValidationError , match = '2 validation errors for Lng' ):
133160 Lng (lng = longitude )
134161
135162
136163def test_str_repr ():
164+ # Float tests
137165 assert str (Coord (coord = (20.0 , 10.0 )).coord ) == '20.0,10.0'
138166 assert str (Coord (coord = ('20.0, 10.0' )).coord ) == '20.0,10.0'
139167 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+ )
140175
141176
142177def test_eq ():
178+ # Float tests
143179 assert Coord (coord = (20.0 , 10.0 )).coord != Coord (coord = '20.0,11.0' ).coord
144180 assert Coord (coord = ('20.0, 10.0' )).coord != Coord (coord = '20.0,11.0' ).coord
145181 assert Coord (coord = ('20.0, 10.0' )).coord != Coord (coord = '20.0,11.0' ).coord
146182 assert Coord (coord = (20.0 , 10.0 )).coord == Coord (coord = '20.0,10.0' ).coord
147183
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+
148195
149196def test_hashable ():
197+ # Float tests
150198 assert hash (Coord (coord = (20.0 , 10.0 )).coord ) == hash (Coord (coord = (20.0 , 10.0 )).coord )
151199 assert hash (Coord (coord = (20.0 , 11.0 )).coord ) != hash (Coord (coord = (20.0 , 10.0 )).coord )
152200
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+
153213
154214def test_json_schema ():
155215 class Model (BaseModel ):
156216 value : Coordinate
157217
158218 assert Model .model_json_schema (mode = 'validation' )['$defs' ]['Coordinate' ] == {
159219 '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+ },
162228 },
163229 'required' : ['latitude' , 'longitude' ],
164230 'title' : 'Coordinate' ,
@@ -170,7 +236,10 @@ class Model(BaseModel):
170236 {
171237 'maxItems' : 2 ,
172238 'minItems' : 2 ,
173- 'prefixItems' : [{'type' : 'number' }, {'type' : 'number' }],
239+ 'prefixItems' : [
240+ {'anyOf' : [{'type' : 'number' }, {'type' : 'string' }]},
241+ {'anyOf' : [{'type' : 'number' }, {'type' : 'string' }]},
242+ ],
174243 'type' : 'array' ,
175244 },
176245 {'type' : 'string' },
@@ -181,8 +250,14 @@ class Model(BaseModel):
181250 '$defs' : {
182251 'Coordinate' : {
183252 '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+ },
186261 },
187262 'required' : ['latitude' , 'longitude' ],
188263 'title' : 'Coordinate' ,
0 commit comments