44from enum import Enum
55from typing import (
66 TYPE_CHECKING ,
7- Generic ,
7+ Any ,
88 List ,
99 Literal ,
10+ MutableSequence ,
1011 Sequence ,
1112 Tuple ,
12- TypeVar ,
1313 Union ,
1414)
1515
1616from geojson_pydantic .geometries import Geometry , GeometryCollection
1717from geojson_pydantic .types import BBox
18- from pydantic import BaseModel , StrictBool , StrictFloat , StrictInt , StrictStr , conlist
19- from pydantic .generics import GenericModel
18+ from pydantic import (
19+ BaseModel ,
20+ Field ,
21+ RootModel ,
22+ StrictBool ,
23+ StrictFloat ,
24+ StrictInt ,
25+ StrictStr ,
26+ )
27+ from typing_extensions import Annotated
2028
2129# The use of `Strict*` is necessary in a few places because pydantic will convert
2230# booleans to numbers and vice versa. As well as various types to strings. This
@@ -130,18 +138,18 @@ def __str__(self) -> str:
130138 return f"{ self .op .upper ()} ({ self .args [0 ]} , { self .args [1 ]} )"
131139
132140
133- class Array (BaseModel ):
134- __root__ : List [ArrayElement ]
141+ class Array (RootModel ):
142+ root : MutableSequence [ArrayElement ]
135143
136144 def __str__ (self ) -> str :
137- return f"({ join_list (self .__root__ , ', ' )} )"
145+ return f"({ join_list (self .root , ', ' )} )"
138146
139147
140- class ArrayExpression (BaseModel ):
141- __root__ : Tuple [ArrayExpressionItems , ArrayExpressionItems ]
148+ class ArrayExpression (RootModel ):
149+ root : Tuple [ArrayExpressionItems , ArrayExpressionItems ]
142150
143151 def __str__ (self ) -> str :
144- return f"({ self .__root__ [0 ]} , { self .__root__ [1 ]} )"
152+ return f"({ self .root [0 ]} , { self .root [1 ]} )"
145153
146154
147155class ArrayOperator (str , Enum ):
@@ -159,15 +167,15 @@ def __str__(self) -> str:
159167 return f"{ self .op .upper ()} { self .args } "
160168
161169
162- class BooleanExpression (BaseModel ):
163- __root__ : BooleanExpressionItems
170+ class BooleanExpression (RootModel ):
171+ root : BooleanExpressionItems
164172
165173 def __str__ (self ) -> str :
166174 # If it's a bool, we uppercase it.
167- if isinstance (self .__root__ , bool ):
168- return str (self .__root__ ).upper ()
175+ if isinstance (self .root , bool ):
176+ return str (self .root ).upper ()
169177 # Otherwise, we return the string representation of the root.
170- return str (self .__root__ )
178+ return str (self .root )
171179
172180
173181# Type checking does not like `conlist`, so use a conditional here to avoid the error.
@@ -176,7 +184,7 @@ def __str__(self) -> str:
176184if TYPE_CHECKING :
177185 BooleanExpressionList = List [BooleanExpression ]
178186else :
179- BooleanExpressionList = conlist ( item_type = BooleanExpression , min_items = 2 )
187+ BooleanExpressionList = Annotated [ List [ BooleanExpression ], Field ( min_length = 2 )]
180188
181189
182190class AndOrExpression (BaseModel ):
@@ -245,21 +253,21 @@ def __str__(self) -> str:
245253 return f"BBOX{ self .bbox } "
246254
247255
248- class IntervalArrayItems (BaseModel ):
249- __root__ : Union [datetime , date , Literal [".." ], PropertyRef , FunctionRef ]
256+ class IntervalArrayItems (RootModel ):
257+ root : Union [datetime , date , Literal [".." ], PropertyRef , FunctionRef ]
250258
251259 def __str__ (self ) -> str :
252260 # If it is a datetime, format it as iso with `Z` at the end. Note this will
253261 # always include the microseconds, even if they are 0.
254- if isinstance (self .__root__ , datetime ):
255- return f"""'{ self .__root__ .strftime ("%Y-%m-%dT%H:%M:%S.%fZ" )} '"""
262+ if isinstance (self .root , datetime ):
263+ return f"""'{ self .root .strftime ("%Y-%m-%dT%H:%M:%S.%fZ" )} '"""
256264 # If it is a date, format with built-in isoformat
257- if isinstance (self .__root__ , date ):
258- return f"'{ self .__root__ .isoformat ()} '"
259- if self .__root__ == ".." :
265+ if isinstance (self .root , date ):
266+ return f"'{ self .root .isoformat ()} '"
267+ if self .root == ".." :
260268 return "'..'"
261269 # Otherwise use the string representation of the root.
262- return str (self .__root__ )
270+ return str (self .root )
263271
264272
265273class IntervalInstance (BaseModel ):
@@ -269,50 +277,60 @@ def __str__(self) -> str:
269277 return f"INTERVAL({ self .interval [0 ]} , { self .interval [1 ]} )"
270278
271279
272- class CharacterExpression (BaseModel ):
273- __root__ : CharacterExpressionItems
280+ class Casei (BaseModel ):
281+ casei : Union [ CharacterExpression , PatternExpression ]
274282
275283 def __str__ (self ) -> str :
276- # If it is already a string, make it a char literal
277- if isinstance (self .__root__ , str ):
278- return make_char_literal (self .__root__ )
279- # Otherwise, return the string representation of the root
280- return str (self .__root__ )
284+ return f"CASEI({ self .casei } )"
281285
282286
283- class PatternExpression (BaseModel ):
284- __root__ : PatternExpressionItems
287+ class CaseiCharacterExpression (Casei ):
288+ casei : CharacterExpression
289+
290+
291+ class CaseiPatternExpression (Casei ):
292+ casei : PatternExpression
293+
294+
295+ class Accenti (BaseModel ):
296+ accenti : Union [CharacterExpression , PatternExpression ]
285297
286298 def __str__ (self ) -> str :
287- # If it is already a string, make it a char literal
288- if isinstance (self .__root__ , str ):
289- return make_char_literal (self .__root__ )
290- # Otherwise, return the string representation of the root
291- return str (self .__root__ )
299+ return f"ACCENTI({ self .accenti } )"
300+
292301
302+ class AccentiCharacterExpression (Accenti ):
303+ accenti : CharacterExpression
293304
294- E = TypeVar ("E" , bound = Union [CharacterExpression , PatternExpression ])
295305
306+ class AccentiPatternExpression (Accenti ):
307+ accenti : PatternExpression
296308
297- class Casei (GenericModel , Generic [E ]):
298- casei : E
309+
310+ class GeometryLiteral (RootModel ):
311+ root : Union [Geometry , GeometryCollection ]
299312
300313 def __str__ (self ) -> str :
301- return f"CASEI( { self .casei } )"
314+ return self .root . wkt
302315
303316
304- class Accenti ( GenericModel , Generic [ E ] ):
305- accenti : E
317+ class CharLiteralRootModel ( RootModel ):
318+ root : Any
306319
307320 def __str__ (self ) -> str :
308- return f"ACCENTI({ self .accenti } )"
321+ # If it is already a string, make it a char literal
322+ if isinstance (self .root , str ):
323+ return make_char_literal (self .root )
324+ # Otherwise, return the string representation of the root
325+ return str (self .root )
309326
310327
311- class GeometryLiteral ( BaseModel ):
312- __root__ : Union [ Geometry , GeometryCollection ]
328+ class CharacterClause ( CharLiteralRootModel ):
329+ root : CharacterClauseItems
313330
314- def __str__ (self ) -> str :
315- return self .__root__ .wkt
331+
332+ class PatternExpression (CharLiteralRootModel ):
333+ root : PatternExpressionItems
316334
317335
318336ComparisonPredicate = Union [
@@ -332,33 +350,36 @@ def __str__(self) -> str:
332350TemporalExpression = Union [TemporalInstance , PropertyRef , FunctionRef ]
333351TemporalInstantExpression = Union [InstantInstance , PropertyRef , FunctionRef ]
334352ScalarExpression = Union [
335- TemporalInstantExpression , BooleanExpression , CharacterExpression , NumericExpression
353+ TemporalInstantExpression , BooleanExpression , CharacterClause , NumericExpression
336354]
337355ArithmeticOperandsItems = Union [
338356 ArithmeticExpression , PropertyRef , FunctionRef , StrictFloatOrInt
339357]
340358ArrayExpressionItems = Union [Array , PropertyRef , FunctionRef ]
341359PatternExpressionItems = Union [
342- Casei [ PatternExpression ], Accenti [ PatternExpression ] , StrictStr
360+ CaseiPatternExpression , AccentiPatternExpression , StrictStr
343361]
344- CharacterExpressionItems = Union [
345- Casei [ CharacterExpression ] ,
346- Accenti [ CharacterExpression ] ,
362+ CharacterClauseItems = Union [
363+ CaseiCharacterExpression ,
364+ AccentiCharacterExpression ,
347365 StrictStr ,
366+ ]
367+ CharacterExpression = Union [
368+ CharacterClause ,
348369 PropertyRef ,
349370 FunctionRef ,
350371]
351372
352373# Extra types to match the cql2-text grammar better
353374IsNullOperand = Union [
354- CharacterExpression ,
375+ CharacterClause ,
355376 NumericExpression ,
356377 TemporalExpression ,
357378 BooleanExpression ,
358379 GeomExpression ,
359380]
360381ArrayElement = Union [
361- CharacterExpression ,
382+ CharacterClause ,
362383 NumericExpression ,
363384 BooleanExpression ,
364385 GeomExpression ,
@@ -369,7 +390,7 @@ def __str__(self) -> str:
369390 None ,
370391 List [
371392 Union [
372- CharacterExpression ,
393+ CharacterClause ,
373394 NumericExpression ,
374395 BooleanExpression ,
375396 GeomExpression ,
@@ -389,28 +410,29 @@ def __str__(self) -> str:
389410]
390411
391412# Update all the forward references
392- Accenti .update_forward_refs ()
393- AndOrExpression .update_forward_refs ()
394- ArithmeticExpression .update_forward_refs ()
395- ArrayExpression .update_forward_refs ()
396- Array .update_forward_refs ()
397- ArrayPredicate .update_forward_refs ()
398- BboxLiteral .update_forward_refs ()
399- BinaryComparisonPredicate .update_forward_refs ()
400- BooleanExpression .update_forward_refs ()
401- Casei .update_forward_refs ()
402- CharacterExpression .update_forward_refs ()
403- DateInstant .update_forward_refs ()
404- Function .update_forward_refs ()
405- FunctionRef .update_forward_refs ()
406- IntervalInstance .update_forward_refs ()
407- IsBetweenPredicate .update_forward_refs ()
408- IsInListPredicate .update_forward_refs ()
409- IsLikePredicate .update_forward_refs ()
410- IsNullPredicate .update_forward_refs ()
411- NotExpression .update_forward_refs ()
412- PatternExpression .update_forward_refs ()
413- PropertyRef .update_forward_refs ()
414- SpatialPredicate .update_forward_refs ()
415- TemporalPredicate .update_forward_refs ()
416- TimestampInstant .update_forward_refs ()
413+ AccentiCharacterExpression .model_rebuild ()
414+ AccentiPatternExpression .model_rebuild ()
415+ AndOrExpression .model_rebuild ()
416+ ArithmeticExpression .model_rebuild ()
417+ ArrayExpression .model_rebuild ()
418+ ArrayPredicate .model_rebuild ()
419+ BboxLiteral .model_rebuild ()
420+ BinaryComparisonPredicate .model_rebuild ()
421+ BooleanExpression .model_rebuild ()
422+ CaseiCharacterExpression .model_rebuild ()
423+ CaseiPatternExpression .model_rebuild ()
424+ CharacterClause .model_rebuild ()
425+ DateInstant .model_rebuild ()
426+ Function .model_rebuild ()
427+ FunctionRef .model_rebuild ()
428+ IntervalInstance .model_rebuild ()
429+ IsBetweenPredicate .model_rebuild ()
430+ IsInListPredicate .model_rebuild ()
431+ IsLikePredicate .model_rebuild ()
432+ IsNullPredicate .model_rebuild ()
433+ NotExpression .model_rebuild ()
434+ PatternExpression .model_rebuild ()
435+ PropertyRef .model_rebuild ()
436+ SpatialPredicate .model_rebuild ()
437+ TemporalPredicate .model_rebuild ()
438+ TimestampInstant .model_rebuild ()
0 commit comments