Skip to content

Commit 460eee7

Browse files
authored
Check kind of type in Python (#909)
Moves the kind of type detection from the query for reflection from EdgeQL to Python because the computed property use in the query turned out to be too costly: ``` ─────────────────────────── Query ─────────────────────────── analyze with module schema select ➊ Type { ➋ kind := assert_exists( 'Object' IF Type IS ObjectType ELSE ➌ 'Scalar' IF Type IS ScalarType ELSE ➍ 'Array' IF Type IS Array ELSE 'NamedTuple' IF ➎ Type[IS Tuple].named ?? false ELSE ➏ 'Tuple' IF Type IS Tuple ELSE ➐ 'Range' IF Type IS Range ELSE ➑ 'MultiRange' IF Type IS MultiRange ELSE ➒ 'Pseudo' IF Type IS PseudoType ELSE <str>{}, message := "unexpected type", ), }; ────────────────────────────────────────────── Coarse-grained Query Plan ────────────────────────────────────────────── │ Time Cost Loops Rows Width │ Relations root │ 2282.4 837696.69 1.0 248.0 32 │ schema::ArrayExprAlias, schema::Tuple, schema::ScalarType, │ │ schema::Range, schema::RangeExprAlias, schema::MultiRange, │ │ schema::TupleExprAlias, schema::PseudoType, schema::ObjectType, │ │ schema::MultiRangeExprAlias, schema::Array ``` vs ``` ──────────────────────── Query ──────────────────────── analyze with module schema select ➊ Type { ➋ is_object := Type IS ObjectType, ➌ is_scalar := Type IS ScalarType, ➍ is_array := Type IS Array, ➎ is_named_tuple := Type[IS Tuple].named ?? false, ➏ is_tuple := Type IS Tuple, ➐ is_range := Type IS Range, ➑ is_multi_range := Type IS MultiRange, ➒ is_pseudo := Type IS PseudoType, }; ────────────────────────────────────────────── Coarse-grained Query Plan ────────────────────────────────────────────── │ Time Cost Loops Rows Width │ Relations root │ 210.6 6316.56 1.0 248.0 32 │ schema::ArrayExprAlias, schema::Tuple, schema::ScalarType, schema::Range, │ │ schema::RangeExprAlias, schema::MultiRange, schema::TupleExprAlias, │ │ schema::PseudoType, schema::ObjectType, schema::MultiRangeExprAlias, │ │ schema::Array ```
1 parent 3e66fe5 commit 460eee7

File tree

3 files changed

+148
-24
lines changed

3 files changed

+148
-24
lines changed

gel/_internal/_reflection/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
is_range_type,
8484
is_scalar_type,
8585
is_tuple_type,
86+
kind_of_type,
8687
)
8788

8889
from ._modules import (
@@ -147,4 +148,5 @@
147148
"is_range_type",
148149
"is_scalar_type",
149150
"is_tuple_type",
151+
"kind_of_type",
150152
)

gel/_internal/_reflection/_query.py

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -164,23 +164,19 @@
164164
)
165165
166166
SELECT Type {
167-
kind := assert_exists(
168-
'Object' IF Type IS ObjectType ELSE
169-
'Scalar' IF Type IS ScalarType ELSE
170-
'Array' IF Type IS Array ELSE
171-
'NamedTuple' IF Type[IS Tuple].named ?? false ELSE
172-
'Tuple' IF Type IS Tuple ELSE
173-
'Range' IF Type IS Range ELSE
174-
'MultiRange' IF Type IS MultiRange ELSE
175-
'Pseudo' IF Type IS PseudoType ELSE
176-
<str>{},
177-
message := "unexpected type",
178-
),
179-
180167
id,
181168
builtin,
182169
internal,
183170
171+
is_object := Type IS ObjectType,
172+
is_scalar := Type IS ScalarType,
173+
is_array := Type IS Array,
174+
is_named_tuple := Type[IS Tuple].named ?? false,
175+
is_tuple := Type IS Tuple,
176+
is_range := Type IS Range,
177+
is_multi_range := Type IS MultiRange,
178+
is_pseudo := Type IS PseudoType,
179+
184180
name := (
185181
array_join(array_agg([IS ObjectType].union_of.name), ' | ')
186182
IF EXISTS [IS ObjectType].union_of

gel/_internal/_reflection/_types.py

Lines changed: 137 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,22 @@ def __str__(self) -> str:
4848

4949
@sobject
5050
class Type(SchemaObject, abc.ABC):
51-
kind: TypeKind
5251
builtin: bool
5352
internal: bool
5453

54+
is_object: bool
55+
is_scalar: bool
56+
is_array: bool
57+
is_named_tuple: bool
58+
is_tuple: bool
59+
is_range: bool
60+
is_multi_range: bool
61+
is_pseudo: bool
62+
63+
@functools.cached_property
64+
def kind(self) -> TypeKind:
65+
return kind_of_type(self)
66+
5567
def __str__(self) -> str:
5668
return self.name
5769

@@ -221,7 +233,18 @@ def issubclass(self, parent: Self) -> bool:
221233

222234
@struct
223235
class PseudoType(Type):
224-
kind: Literal[TypeKind.Pseudo]
236+
is_object: Literal[False]
237+
is_scalar: Literal[False]
238+
is_array: Literal[False]
239+
is_named_tuple: Literal[False]
240+
is_tuple: Literal[False]
241+
is_range: Literal[False]
242+
is_multi_range: Literal[False]
243+
is_pseudo: Literal[True]
244+
245+
@functools.cached_property
246+
def kind(self) -> Literal[TypeKind.Pseudo]:
247+
return TypeKind.Pseudo
225248

226249
@functools.cached_property
227250
def generic(self) -> bool:
@@ -246,23 +269,47 @@ def _assignable_from(
246269

247270
@struct
248271
class ScalarType(InheritingType):
249-
kind: Literal[TypeKind.Scalar]
250272
is_seq: bool
251273
enum_values: tuple[str, ...] | None = None
252274
material_id: str | None = None
253275
cast_type: str | None = None
254276

277+
is_object: Literal[False]
278+
is_scalar: Literal[True]
279+
is_array: Literal[False]
280+
is_named_tuple: Literal[False]
281+
is_tuple: Literal[False]
282+
is_range: Literal[False]
283+
is_multi_range: Literal[False]
284+
is_pseudo: Literal[False]
285+
286+
@functools.cached_property
287+
def kind(self) -> Literal[TypeKind.Scalar]:
288+
return TypeKind.Scalar
289+
255290

256291
@struct
257292
class ObjectType(InheritingType):
258-
kind: Literal[TypeKind.Object]
259293
union_of: tuple[TypeRef, ...]
260294
intersection_of: tuple[TypeRef, ...]
261295
compound_type: bool
262296
# N.B: We store Pointers sorted by name, from the
263297
# introspection query
264298
pointers: tuple[Pointer, ...]
265299

300+
is_object: Literal[True]
301+
is_scalar: Literal[False]
302+
is_array: Literal[False]
303+
is_named_tuple: Literal[False]
304+
is_tuple: Literal[False]
305+
is_range: Literal[False]
306+
is_multi_range: Literal[False]
307+
is_pseudo: Literal[False]
308+
309+
@functools.cached_property
310+
def kind(self) -> Literal[TypeKind.Object]:
311+
return TypeKind.Object
312+
266313
def get_pointer(self, name: str) -> Pointer:
267314
for ptr in self.pointers:
268315
if ptr.name == name:
@@ -506,9 +553,21 @@ def _assignable_from(
506553

507554
@struct
508555
class ArrayType(HomogeneousCollectionType):
509-
kind: Literal[TypeKind.Array]
510556
array_element_id: str
511557

558+
is_object: Literal[False]
559+
is_scalar: Literal[False]
560+
is_array: Literal[True]
561+
is_named_tuple: Literal[False]
562+
is_tuple: Literal[False]
563+
is_range: Literal[False]
564+
is_multi_range: Literal[False]
565+
is_pseudo: Literal[False]
566+
567+
@functools.cached_property
568+
def kind(self) -> Literal[TypeKind.Array]:
569+
return TypeKind.Array
570+
512571
@functools.cached_property
513572
def _element_type_id(self) -> str:
514573
return self.array_element_id
@@ -520,7 +579,19 @@ def get_id_and_name(self, element_type: Type) -> tuple[str, str]:
520579

521580
@struct
522581
class RangeType(HomogeneousCollectionType):
523-
kind: Literal[TypeKind.Range]
582+
is_object: Literal[False]
583+
is_scalar: Literal[False]
584+
is_array: Literal[False]
585+
is_named_tuple: Literal[False]
586+
is_tuple: Literal[False]
587+
is_range: Literal[True]
588+
is_multi_range: Literal[False]
589+
is_pseudo: Literal[False]
590+
591+
@functools.cached_property
592+
def kind(self) -> Literal[TypeKind.Range]:
593+
return TypeKind.Range
594+
524595
range_element_id: str
525596

526597
@functools.cached_property
@@ -534,7 +605,19 @@ def get_id_and_name(self, element_type: Type) -> tuple[str, str]:
534605

535606
@struct
536607
class MultiRangeType(HomogeneousCollectionType):
537-
kind: Literal[TypeKind.MultiRange]
608+
is_object: Literal[False]
609+
is_scalar: Literal[False]
610+
is_array: Literal[False]
611+
is_named_tuple: Literal[False]
612+
is_tuple: Literal[False]
613+
is_range: Literal[False]
614+
is_multi_range: Literal[True]
615+
is_pseudo: Literal[False]
616+
617+
@functools.cached_property
618+
def kind(self) -> Literal[TypeKind.MultiRange]:
619+
return TypeKind.MultiRange
620+
538621
multirange_element_id: str
539622

540623
@functools.cached_property
@@ -563,7 +646,18 @@ def _element_type_ids(self) -> list[str]:
563646

564647
@struct
565648
class TupleType(_TupleType):
566-
kind: Literal[TypeKind.Tuple]
649+
is_object: Literal[False]
650+
is_scalar: Literal[False]
651+
is_array: Literal[False]
652+
is_named_tuple: Literal[False]
653+
is_tuple: Literal[True]
654+
is_range: Literal[False]
655+
is_multi_range: Literal[False]
656+
is_pseudo: Literal[False]
657+
658+
@functools.cached_property
659+
def kind(self) -> Literal[TypeKind.Tuple]:
660+
return TypeKind.Tuple
567661

568662
def get_id_and_name(
569663
self, element_types: tuple[Type, ...]
@@ -576,7 +670,18 @@ def get_id_and_name(
576670

577671
@struct
578672
class NamedTupleType(_TupleType):
579-
kind: Literal[TypeKind.NamedTuple]
673+
is_object: Literal[False]
674+
is_scalar: Literal[False]
675+
is_array: Literal[False]
676+
is_named_tuple: Literal[True]
677+
is_tuple: Literal[False]
678+
is_range: Literal[False]
679+
is_multi_range: Literal[False]
680+
is_pseudo: Literal[False]
681+
682+
@functools.cached_property
683+
def kind(self) -> Literal[TypeKind.NamedTuple]:
684+
return TypeKind.NamedTuple
580685

581686
def get_id_and_name(
582687
self, element_types: tuple[Type, ...]
@@ -661,7 +766,7 @@ def get_nearest_common_ancestors(
661766
| MultiRangeType
662767
)
663768

664-
AnyType = PrimitiveType | PseudoType | ObjectType
769+
AnyType = PrimitiveType | ObjectType | PseudoType
665770

666771
Types = dict[str, Type]
667772

@@ -722,6 +827,27 @@ def is_primitive_type(t: Type) -> TypeGuard[PrimitiveType]:
722827
return not isinstance(t, (ObjectType, PseudoType))
723828

724829

830+
def kind_of_type(ty: Type) -> TypeKind:
831+
if ty.is_object:
832+
return TypeKind.Object
833+
elif ty.is_scalar:
834+
return TypeKind.Scalar
835+
elif ty.is_array:
836+
return TypeKind.Array
837+
elif ty.is_named_tuple:
838+
return TypeKind.NamedTuple
839+
elif ty.is_tuple:
840+
return TypeKind.Tuple
841+
elif ty.is_range:
842+
return TypeKind.Range
843+
elif ty.is_multi_range:
844+
return TypeKind.MultiRange
845+
elif ty.is_pseudo:
846+
return TypeKind.Pseudo
847+
else:
848+
raise ValueError("unexpected type")
849+
850+
725851
@sobject
726852
class Pointer(SchemaObject):
727853
card: Cardinality
@@ -752,7 +878,7 @@ def fetch_types(
752878
types: list[Type] = db.query(_query.TYPES, builtin=builtin)
753879
result = {}
754880
for t in types:
755-
cls = _kind_to_class[t.kind]
881+
cls = _kind_to_class[kind_of_type(t)]
756882
replace: dict[str, Any] = {}
757883
if issubclass(cls, CollectionType):
758884
replace["name"] = _edgeql.unmangle_unqual_name(t.name)

0 commit comments

Comments
 (0)