Skip to content

Commit 7b223ac

Browse files
authored
Rename IsSub to IsAssignable and Matches to IsEquivalent (#80)
Assignability is the right technical term, and unfortunately the abbreviations are fraught.
1 parent d0eb22d commit 7b223ac

13 files changed

Lines changed: 169 additions & 145 deletions

pep.rst

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -579,18 +579,15 @@ proposing to add that as a notion, but we could.
579579
Boolean operators
580580
'''''''''''''''''
581581

582-
* ``IsSub[T, S]``: Returns a boolean literal type indicating whether
583-
``T`` is a subtype of ``S``.
582+
* ``IsAssignable[T, S]``: Returns a boolean literal type indicating whether
583+
``S`` is assignable to ``T``.
584584

585-
(Actually, I suppose, whether ``S`` is "assignable to" ``T``. We can
586-
certainly bikeshed on this name.)
587-
588-
* ``Matches[T, S]``:
589-
Equivalent to ``IsSub[T, S] and IsSub[S, T]``.
585+
* ``IsEquivalent[T, S]``:
586+
Equivalent to ``IsAssignable[T, S] and IsAssignable[S, T]``.
590587

591588
* ``Bool[T]``: Returns ``Literal[True]`` if ``T`` is also
592589
``Literal[True]`` or a union containing it.
593-
Equivalent to ``IsSub[T, Literal[True]] and not IsSub[T, Never]``.
590+
Equivalent to ``IsAssignable[T, Literal[True]] and not IsAssignable[T, Never]``.
594591

595592
This is useful for invoking "helper aliases" that return a boolean
596593
literal type.
@@ -1021,7 +1018,7 @@ producing a new target type containing only properties and wrapping
10211018

10221019
type ConvertField[T] = (
10231020
AdjustLink[PropsOnly[PointerArg[T]], T]
1024-
if typing.IsSub[T, Link]
1021+
if typing.IsAssignable[T, Link]
10251022
else PointerArg[T]
10261023
)
10271024

@@ -1044,7 +1041,7 @@ we've discussed already.
10441041
::
10451042

10461043
type AdjustLink[Tgt, LinkTy] = (
1047-
list[Tgt] if typing.IsSub[LinkTy, MultiLink] else Tgt
1044+
list[Tgt] if typing.IsAssignable[LinkTy, MultiLink] else Tgt
10481045
)
10491046

10501047
And the final helper, ``PropsOnly[T]``, generates a new type that
@@ -1056,7 +1053,7 @@ contains all the ``Property`` attributes of ``T``.
10561053
*[
10571054
typing.Member[typing.GetName[p], PointerArg[typing.GetType[p]]]
10581055
for p in typing.Iter[typing.Attrs[T]]
1059-
if typing.IsSub[typing.GetType[p], Property]
1056+
if typing.IsAssignable[typing.GetType[p], Property]
10601057
]
10611058
]
10621059

@@ -1078,7 +1075,7 @@ suite, but here is a possible implementation of just ``Public``
10781075
# otherwise we return the type itself.
10791076
type GetDefault[Init] = (
10801077
GetFieldItem[Init, Literal["default"]]
1081-
if typing.IsSub[Init, Field]
1078+
if typing.IsAssignable[Init, Field]
10821079
else Init
10831080
)
10841081

@@ -1092,7 +1089,7 @@ suite, but here is a possible implementation of just ``Public``
10921089
GetDefault[typing.GetInit[p]],
10931090
]
10941091
for p in typing.Iter[typing.Attrs[T]]
1095-
if not typing.IsSub[
1092+
if not typing.IsAssignable[
10961093
Literal[True],
10971094
GetFieldItem[typing.GetInit[p], Literal["primary_key"]],
10981095
]
@@ -1131,7 +1128,7 @@ dataclasses-style method generation
11311128
# All arguments are keyword-only
11321129
# It takes a default if a default is specified in the class
11331130
Literal["keyword"]
1134-
if typing.IsSub[
1131+
if typing.IsAssignable[
11351132
GetDefault[typing.GetInit[p]],
11361133
Never,
11371134
]
@@ -1165,9 +1162,9 @@ NumPy-style broadcasting
11651162

11661163
type MergeOne[T, S] = (
11671164
T
1168-
if typing.Matches[T, S] or typing.Matches[S, Literal[1]]
1165+
if typing.IsEquivalent[T, S] or typing.IsEquivalent[S, Literal[1]]
11691166
else S
1170-
if typing.Matches[T, Literal[1]]
1167+
if typing.IsEquivalent[T, Literal[1]]
11711168
else typing.RaiseError[Literal["Broadcast mismatch"], T, S]
11721169
)
11731170

@@ -1176,7 +1173,7 @@ NumPy-style broadcasting
11761173

11771174
# Matching on Never here is intentional; it prevents infinite
11781175
# recursions when T is not a tuple.
1179-
type Empty[T] = typing.IsSub[typing.Length[T], Literal[0]]
1176+
type Empty[T] = typing.IsAssignable[typing.Length[T], Literal[0]]
11801177

11811178
type Broadcast[T, S] = (
11821179
S
@@ -1227,7 +1224,7 @@ Generic Callable
12271224

12281225
Consider a method with the following signature::
12291226

1230-
def process[T](self, x: T) -> T if IsSub[T, list] else list[T]:
1227+
def process[T](self, x: T) -> T if IsAssignable[T, list] else list[T]:
12311228
...
12321229

12331230
The type of the method is generic, and the generic is bound at the
@@ -1240,13 +1237,13 @@ unbound type variables and let them be generalized::
12401237
type Foo = NewProtocol[
12411238
Member[
12421239
Literal["process"],
1243-
Callable[[T], set[T] if IsSub[T, int] else T]
1240+
Callable[[T], set[T] if IsAssignable[T, int] else T]
12441241
]
12451242
]
12461243

12471244
The problem is that this is basically incompatible with runtime
12481245
evaluation support, since evaluating the alias ``Foo`` will need to
1249-
evaluate the ``IsSub``, and so we will lose one side of the
1246+
evaluate the ``IsAssignable``, and so we will lose one side of the
12501247
conditional at least. Similar problems will happen when evaluating
12511248
``Members`` on a class with generic functions. By wrapping the body
12521249
in a lambda, we can delay evaluation in both of these cases. (The
@@ -1356,8 +1353,8 @@ the model more complicated, but a lot of code will read much nicer.
13561353
TODO: Should we do this?
13571354

13581355

1359-
Replace ``IsSub`` with something weaker than "assignable to" checking
1360-
---------------------------------------------------------------------
1356+
Replace ``IsAssignable`` with something weaker than "assignable to" checking
1357+
----------------------------------------------------------------------------
13611358

13621359
Full python typing assignability checking is not fully implementable
13631360
at runtime (in particular, even if all the typeshed types for the
@@ -1371,15 +1368,15 @@ ideally with the contours of that effort well-documented.
13711368
An alternative approach would be to have a weaker predicate as the
13721369
core primitive.
13731370

1374-
One possibility would be a "sub-similarity" check: ``IsSubSimilar``
1371+
One possibility would be a "sub-similarity" check: ``IsAssignableSimilar``
13751372
would do *simple* checking of the *head* of types, essentially,
13761373
without looking at type parameters. It would not work with protocols.
13771374
It would still lift over unions and would check literals.
13781375

13791376
We decided it probably was not a good idea to introduce a new notion
13801377
that is similar to but not the same as subtyping, and that would need
1381-
to either have a long and weird name like ``IsSubSimilar`` or a
1382-
misleading short one like ``IsSub``.
1378+
to either have a long and weird name like ``IsAssignableSimilar`` or a
1379+
misleading short one like ``IsAssignable``.
13831380

13841381
.. _less_syntax:
13851382

tests/test_astlike_1.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
GetArg,
1212
GetName,
1313
GetType,
14-
IsSub,
14+
IsAssignable,
1515
Iter,
16-
Matches,
16+
IsEquivalent,
1717
Member,
1818
NewProtocol,
1919
RaiseError,
@@ -52,16 +52,16 @@
5252
(
5353
VarArgType[x]
5454
if not any( # Unique to Ls
55-
Matches[VarArgName[x], VarArgName[y]] for y in Iter[Rs]
55+
IsEquivalent[VarArgName[x], VarArgName[y]] for y in Iter[Rs]
5656
)
5757
else GetArg[ # Common to both Ls and Rs
5858
tuple[
5959
*[
6060
(
6161
VarArgType[x]
62-
if IsSub[VarArgType[x], VarArgType[y]]
62+
if IsAssignable[VarArgType[x], VarArgType[y]]
6363
else VarArgType[y]
64-
if IsSub[VarArgType[y], VarArgType[x]]
64+
if IsAssignable[VarArgType[y], VarArgType[x]]
6565
else RaiseError[
6666
typing.Literal[
6767
"Type mismatch for variable"
@@ -72,7 +72,7 @@
7272
]
7373
)
7474
for y in Iter[Rs]
75-
if Matches[VarArgName[x], VarArgName[y]]
75+
if IsEquivalent[VarArgName[x], VarArgName[y]]
7676
]
7777
],
7878
tuple,
@@ -86,7 +86,7 @@
8686
x
8787
for x in Iter[Rs]
8888
if not any( # Unique to Rs
89-
Matches[VarArgName[x], VarArgName[y]] for y in Iter[Ls]
89+
IsEquivalent[VarArgName[x], VarArgName[y]] for y in Iter[Ls]
9090
)
9191
],
9292
]
@@ -133,14 +133,14 @@ def test_astlike_1_combine_varargs_02():
133133
)
134134

135135

136-
type IsAssignable[L, R] = (
137-
IsSub[R, L]
138-
or Bool[Matches[L, float] and Bool[IsFloat[R]]]
139-
or Bool[Matches[L, complex] and Bool[IsComplex[R]]]
136+
type IsNumericAssignable[L, R] = (
137+
IsAssignable[R, L]
138+
or Bool[IsEquivalent[L, float] and Bool[IsFloat[R]]]
139+
or Bool[IsEquivalent[L, complex] and Bool[IsComplex[R]]]
140140
)
141141
type VarIsPresent[V: VarArg, K: BaseTypedDict] = any(
142-
Matches[VarArgName[V], GetName[x]]
143-
and Bool[IsAssignable[VarArgType[V], GetType[x]]]
142+
IsEquivalent[VarArgName[V], GetName[x]]
143+
and Bool[IsNumericAssignable[VarArgType[V], GetType[x]]]
144144
for x in Iter[Attrs[K]]
145145
)
146146
type AllVarsPresent[Vs: tuple[VarArg, ...], K: BaseTypedDict] = all(
@@ -201,9 +201,9 @@ def test_astlike_1_all_vars_present_05():
201201
assert t == _BoolLiteral[False]
202202

203203

204-
type IsIntegral[T] = IsSub[T, int]
205-
type IsFloat[T] = Bool[IsIntegral[T]] or IsSub[T, float]
206-
type IsComplex[T] = Bool[IsFloat[T]] or IsSub[T, complex]
204+
type IsIntegral[T] = IsAssignable[T, int]
205+
type IsFloat[T] = Bool[IsIntegral[T]] or IsAssignable[T, float]
206+
type IsComplex[T] = Bool[IsFloat[T]] or IsAssignable[T, complex]
207207

208208
type SimpleNumericOp[L, R, OpName: str] = (
209209
int
@@ -229,7 +229,7 @@ def test_astlike_1_all_vars_present_05():
229229
type Mul[L, R] = ComplexNumericOp[L, R, typing.Literal["*"]]
230230
type TrueDiv[L, R] = (
231231
float
232-
if IsSub[L, int] and IsSub[R, int]
232+
if IsAssignable[L, int] and IsAssignable[R, int]
233233
else ComplexNumericOp[L, R, typing.Literal["/"]]
234234
)
235235
type FloorDiv[L, R] = SimpleNumericOp[L, R, typing.Literal["//"]]

tests/test_eval_call_with_types.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
GetArg,
99
GetName,
1010
GetType,
11-
IsSub,
11+
IsAssignable,
1212
Iter,
1313
Members,
1414
Param,
@@ -266,10 +266,10 @@ def func[T](x: C[T]) -> T: ...
266266
GetType[m]
267267
for m in Iter[Members[T]]
268268
if (
269-
IsSub[GetType[m], Callable]
270-
or IsSub[GetType[m], GenericCallable]
269+
IsAssignable[GetType[m], Callable]
270+
or IsAssignable[GetType[m], GenericCallable]
271271
)
272-
and IsSub[GetName[m], N]
272+
and IsAssignable[GetName[m], N]
273273
]
274274
],
275275
tuple,

tests/test_fastapilike_1.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
NewProtocol,
1010
Iter,
1111
Attrs,
12-
IsSub,
12+
IsAssignable,
1313
GetAnnotations,
1414
DropAnnotations,
1515
FromUnion,
@@ -54,7 +54,7 @@ class _Default:
5454
GetName[p],
5555
DropAnnotations[GetType[p]],
5656
Literal["keyword", "default"]
57-
if IsSub[
57+
if IsAssignable[
5858
Literal[PropQuals.HAS_DEFAULT],
5959
GetAnnotations[GetType[p]],
6060
]
@@ -76,14 +76,14 @@ class _Default:
7676
# Strip `| None` from a type by iterating over its union components
7777
# and filtering
7878
type NotOptional[T] = Union[
79-
*[x for x in Iter[FromUnion[T]] if not IsSub[x, None]]
79+
*[x for x in Iter[FromUnion[T]] if not IsAssignable[x, None]]
8080
]
8181

8282
# Adjust an attribute type for use in Public below by dropping | None for
8383
# primary keys and stripping all annotations.
8484
type FixPublicType[T] = DropAnnotations[
8585
NotOptional[T]
86-
if IsSub[Literal[PropQuals.PRIMARY], GetAnnotations[T]]
86+
if IsAssignable[Literal[PropQuals.PRIMARY], GetAnnotations[T]]
8787
else T
8888
]
8989

@@ -94,7 +94,9 @@ class _Default:
9494
*[
9595
Member[GetName[p], FixPublicType[GetType[p]], GetQuals[p]]
9696
for p in Iter[Attrs[T]]
97-
if not IsSub[Literal[PropQuals.HIDDEN], GetAnnotations[GetType[p]]]
97+
if not IsAssignable[
98+
Literal[PropQuals.HIDDEN], GetAnnotations[GetType[p]]
99+
]
98100
]
99101
]
100102

@@ -103,7 +105,9 @@ class _Default:
103105
*[
104106
Member[GetName[p], GetType[p], GetQuals[p]]
105107
for p in Iter[Attrs[T]]
106-
if not IsSub[Literal[PropQuals.PRIMARY], GetAnnotations[GetType[p]]]
108+
if not IsAssignable[
109+
Literal[PropQuals.PRIMARY], GetAnnotations[GetType[p]]
110+
]
107111
]
108112
]
109113

@@ -118,7 +122,9 @@ class _Default:
118122
GetQuals[p],
119123
]
120124
for p in Iter[Attrs[T]]
121-
if not IsSub[Literal[PropQuals.PRIMARY], GetAnnotations[GetType[p]]]
125+
if not IsAssignable[
126+
Literal[PropQuals.PRIMARY], GetAnnotations[GetType[p]]
127+
]
122128
]
123129
]
124130

0 commit comments

Comments
 (0)