From fe4a826029517fb97d8fdaccd39287623ef722de Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Wed, 14 Jan 2026 13:23:50 -0800 Subject: [PATCH] Rename Is -> Sub --- spec-draft.rst | 4 ++-- tests/test_fastapilike_1.py | 16 +++++++++------- tests/test_fastapilike_2.py | 18 ++++++++++-------- tests/test_qblike.py | 6 +++--- tests/test_type_dir.py | 18 +++++++++--------- tests/test_type_eval.py | 10 +++++----- typemap/typing.py | 2 +- 7 files changed, 39 insertions(+), 35 deletions(-) diff --git a/spec-draft.rst b/spec-draft.rst index be9d341..b3d899d 100644 --- a/spec-draft.rst +++ b/spec-draft.rst @@ -94,7 +94,7 @@ It's important that there be a clearly specified type language for the type-leve # Type conditional checks are just boolean compositions of # subtype checking. = - Is[, ] + Sub[, ] | not | and | or @@ -254,7 +254,7 @@ Big (open?) questions --------------------- 1. -Can we actually implement Is (IsSubtype) at runtime in a satisfactory way? (PROBABLE DECISION: external library *and* restricted checking.) +Can we actually implement IsSubtype at runtime in a satisfactory way? (PROBABLE DECISION: external library *and* restricted checking.) - There is a lot that needs to happen, like protocols and variance inference and callable subtyping (which might require matching against type vars...) Jukka points out that lots of type information is frequently missing at runtime too: attributes are frequently unannotated and diff --git a/tests/test_fastapilike_1.py b/tests/test_fastapilike_1.py index d14b2c8..5e35071 100644 --- a/tests/test_fastapilike_1.py +++ b/tests/test_fastapilike_1.py @@ -9,7 +9,7 @@ NewProtocol, Iter, Attrs, - Is, + Sub, GetAnnotations, DropAnnotations, FromUnion, @@ -54,7 +54,7 @@ class _Default: GetName[p], DropAnnotations[GetType[p]], Literal["keyword", "default"] - if Is[ + if Sub[ Literal[PropQuals.HAS_DEFAULT], GetAnnotations[GetType[p]], ] @@ -103,12 +103,14 @@ class _Default: # Strip `| None` from a type by iterating over its union components # and filtering -type NotOptional[T] = Union[*[x for x in Iter[FromUnion[T]] if not Is[x, None]]] +type NotOptional[T] = Union[ + *[x for x in Iter[FromUnion[T]] if not Sub[x, None]] +] # Adjust an attribute type for use in Public below by dropping | None for # primary keys and stripping all annotations. type FixPublicType[T] = DropAnnotations[ - NotOptional[T] if Is[Literal[PropQuals.PRIMARY], GetAnnotations[T]] else T + NotOptional[T] if Sub[Literal[PropQuals.PRIMARY], GetAnnotations[T]] else T ] # Strip out everything that is Hidden and also make the primary key required @@ -118,7 +120,7 @@ class _Default: *[ Member[GetName[p], FixPublicType[GetType[p]], GetQuals[p]] for p in Iter[Attrs[T]] - if not Is[Literal[PropQuals.HIDDEN], GetAnnotations[GetType[p]]] + if not Sub[Literal[PropQuals.HIDDEN], GetAnnotations[GetType[p]]] ] ] @@ -127,7 +129,7 @@ class _Default: *[ Member[GetName[p], GetType[p], GetQuals[p]] for p in Iter[Attrs[T]] - if not Is[Literal[PropQuals.PRIMARY], GetAnnotations[GetType[p]]] + if not Sub[Literal[PropQuals.PRIMARY], GetAnnotations[GetType[p]]] ] ] @@ -142,7 +144,7 @@ class _Default: GetQuals[p], ] for p in Iter[Attrs[T]] - if not Is[Literal[PropQuals.PRIMARY], GetAnnotations[GetType[p]]] + if not Sub[Literal[PropQuals.PRIMARY], GetAnnotations[GetType[p]]] ] ] diff --git a/tests/test_fastapilike_2.py b/tests/test_fastapilike_2.py index 786279d..d545897 100644 --- a/tests/test_fastapilike_2.py +++ b/tests/test_fastapilike_2.py @@ -15,7 +15,7 @@ NewProtocol, Iter, Attrs, - Is, + Sub, FromUnion, GetArg, GetAttr, @@ -53,13 +53,15 @@ class Field[T: FieldArgs](InitField[T]): # Strip `| None` from a type by iterating over its union components # and filtering -type NotOptional[T] = Union[*[x for x in Iter[FromUnion[T]] if not Is[x, None]]] +type NotOptional[T] = Union[ + *[x for x in Iter[FromUnion[T]] if not Sub[x, None]] +] # Adjust an attribute type for use in Public below by dropping | None for # primary keys and stripping all annotations. type FixPublicType[T, Init] = ( NotOptional[T] - if Is[Literal[True], GetFieldItem[Init, Literal["primary_key"]]] + if Sub[Literal[True], GetFieldItem[Init, Literal["primary_key"]]] else T ) @@ -67,7 +69,7 @@ class Field[T: FieldArgs](InitField[T]): # If it is a Field, then we try pulling out the "default" field, # otherwise we return the type itself. type GetDefault[Init] = ( - GetFieldItem[Init, Literal["default"]] if Is[Init, Field] else Init + GetFieldItem[Init, Literal["default"]] if Sub[Init, Field] else Init ) # Strip out everything that is Hidden and also make the primary key required @@ -77,7 +79,7 @@ class Field[T: FieldArgs](InitField[T]): *[ Member[GetName[p], FixPublicType[GetType[p], GetInit[p]], GetQuals[p]] for p in Iter[Attrs[T]] - if not Is[Literal[True], GetFieldItem[GetInit[p], Literal["hidden"]]] + if not Sub[Literal[True], GetFieldItem[GetInit[p], Literal["hidden"]]] ] ] @@ -86,7 +88,7 @@ class Field[T: FieldArgs](InitField[T]): *[ Member[GetName[p], GetType[p], GetQuals[p], GetDefault[GetInit[p]]] for p in Iter[Attrs[T]] - if not Is[ + if not Sub[ Literal[True], GetFieldItem[GetInit[p], Literal["primary_key"]] ] ] @@ -103,7 +105,7 @@ class Field[T: FieldArgs](InitField[T]): Literal[None], ] for p in Iter[Attrs[T]] - if not Is[ + if not Sub[ Literal[True], GetFieldItem[GetInit[p], Literal["primary_key"]] ] ] @@ -124,7 +126,7 @@ class Field[T: FieldArgs](InitField[T]): # All arguments are keyword-only # It takes a default if a default is specified in the class Literal["keyword"] - if Is[ + if Sub[ GetDefault[GetInit[p]], Never, ] diff --git a/tests/test_qblike.py b/tests/test_qblike.py index ff7e65f..f2f872d 100644 --- a/tests/test_qblike.py +++ b/tests/test_qblike.py @@ -8,7 +8,7 @@ NewProtocol, Iter, Attrs, - Is, + Sub, GetType, Member, GetName, @@ -28,11 +28,11 @@ class Link[T]: type PropsOnly[T] = NewProtocol[ - *[p for p in Iter[Attrs[T]] if Is[GetType[p], Property]] + *[p for p in Iter[Attrs[T]] if Sub[GetType[p], Property]] ] # Conditional type alias! -type FilterLinks[T] = Link[PropsOnly[GetArg[T, Link, 0]]] if Is[T, Link] else T +type FilterLinks[T] = Link[PropsOnly[GetArg[T, Link, 0]]] if Sub[T, Link] else T def select[K: BaseTypedDict]( diff --git a/tests/test_type_dir.py b/tests/test_type_dir.py index e2d6052..9a75da0 100644 --- a/tests/test_type_dir.py +++ b/tests/test_type_dir.py @@ -11,7 +11,7 @@ GetQuals, GetType, InitField, - Is, + Sub, Iter, Member, Members, @@ -80,7 +80,7 @@ class Final(Mine, Ordinary, Wrapper[float], AnotherBase[float], Last[int]): pass -type BaseArg[T] = GetArg[T, Base, 0] if Is[T, Base] else Never +type BaseArg[T] = GetArg[T, Base, 0] if Sub[T, Base] else Never type AllOptional[T] = NewProtocol[ @@ -101,7 +101,7 @@ class Final(Mine, Ordinary, Wrapper[float], AnotherBase[float], Last[int]): ] type Prims[T] = NewProtocol[ - *[p for p in Iter[Attrs[T]] if Is[GetType[p], int | str]] + *[p for p in Iter[Attrs[T]] if Sub[GetType[p], int | str]] ] type NoLiterals1[T] = NewProtocol[ @@ -114,7 +114,7 @@ class Final(Mine, Ordinary, Wrapper[float], AnotherBase[float], Last[int]): for t in Iter[FromUnion[GetType[p]]] # XXX: 'typing.Literal' is not *really* a type... # Maybe we can't do this, which maybe is fine. - if not Is[t, Literal] + if not Sub[t, Literal] ] ], GetQuals[p], @@ -130,10 +130,10 @@ class Final(Mine, Ordinary, Wrapper[float], AnotherBase[float], Last[int]): type IsLiteral[T] = ( Literal[True] if ( - (Is[T, str] and not Is[str, T]) - or (Is[T, bytes] and not Is[bytes, T]) - or (Is[T, bool] and not Is[bool, T]) - or (Is[T, int] and not Is[int, T]) + (Sub[T, str] and not Sub[str, T]) + or (Sub[T, bytes] and not Sub[bytes, T]) + or (Sub[T, bool] and not Sub[bool, T]) + or (Sub[T, int] and not Sub[int, T]) # XXX: enum, None ) else Literal[False] @@ -150,7 +150,7 @@ class Final(Mine, Ordinary, Wrapper[float], AnotherBase[float], Last[int]): # XXX: 'typing.Literal' is not *really* a type... # Maybe we can't do this, which maybe is fine. # if not IsSubtype[t, Literal] - if not Is[IsLiteral[t], Literal[True]] + if not Sub[IsLiteral[t], Literal[True]] ] ], GetQuals[p], diff --git a/tests/test_type_eval.py b/tests/test_type_eval.py index 2604263..aef7093 100644 --- a/tests/test_type_eval.py +++ b/tests/test_type_eval.py @@ -26,7 +26,7 @@ GetName, GetType, GetAnnotations, - Is, + Sub, Iter, Length, Member, @@ -63,7 +63,7 @@ class F_int(F[int]): *[ ( Member[GetName[p], OrGotcha[GetType[p]]] - if not Is[GetType[p], A] + if not Sub[GetType[p], A] else Member[GetName[p], OrGotcha[MapRecursive[A]]] ) # XXX: This next line *ought* to work, but we haven't @@ -709,7 +709,7 @@ def test_uppercase_never(): def test_never_is(): - d = eval_typing(Is[Never, Never]) + d = eval_typing(Sub[Never, Never]) assert d is True @@ -769,7 +769,7 @@ def test_eval_literal_idempotent_01(): def test_is_literal_true_vs_one(): - assert eval_typing(Is[Literal[True], Literal[1]]) is False + assert eval_typing(Sub[Literal[True], Literal[1]]) is False def test_callable_to_signature(): @@ -832,7 +832,7 @@ class AnnoTest: def test_type_eval_annotated_02(): - res = eval_typing(Is[GetAttr[AnnoTest, Literal["a"]], int]) + res = eval_typing(Sub[GetAttr[AnnoTest, Literal["a"]], int]) assert res is True diff --git a/typemap/typing.py b/typemap/typing.py index a05ac1e..240e812 100644 --- a/typemap/typing.py +++ b/typemap/typing.py @@ -204,4 +204,4 @@ def IsSubSimilar(self, tps): return _IsGenericAlias(self, tps) -Is = IsSubSimilar +Sub = IsSubSimilar