Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions spec-draft.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
<type-bool> =
Is[<type>, <type>]
Sub[<type>, <type>]
| not <type-bool>
| <type-bool> and <type-bool>
| <type-bool> or <type-bool>
Expand Down Expand Up @@ -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

Expand Down
16 changes: 9 additions & 7 deletions tests/test_fastapilike_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
NewProtocol,
Iter,
Attrs,
Is,
Sub,
GetAnnotations,
DropAnnotations,
FromUnion,
Expand Down Expand Up @@ -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]],
]
Expand Down Expand Up @@ -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
Expand All @@ -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]]]
]
]

Expand All @@ -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]]]
]
]

Expand All @@ -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]]]
]
]

Expand Down
18 changes: 10 additions & 8 deletions tests/test_fastapilike_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
NewProtocol,
Iter,
Attrs,
Is,
Sub,
FromUnion,
GetArg,
GetAttr,
Expand Down Expand Up @@ -53,21 +53,23 @@ 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
)

# Extract the default type from an Init field.
# 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
Expand All @@ -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"]]]
]
]

Expand All @@ -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"]]
]
]
Expand All @@ -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"]]
]
]
Expand All @@ -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,
]
Expand Down
6 changes: 3 additions & 3 deletions tests/test_qblike.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
NewProtocol,
Iter,
Attrs,
Is,
Sub,
GetType,
Member,
GetName,
Expand All @@ -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](
Expand Down
18 changes: 9 additions & 9 deletions tests/test_type_dir.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
GetQuals,
GetType,
InitField,
Is,
Sub,
Iter,
Member,
Members,
Expand Down Expand Up @@ -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[
Expand All @@ -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[
Expand All @@ -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],
Expand All @@ -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]
Expand All @@ -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],
Expand Down
10 changes: 5 additions & 5 deletions tests/test_type_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
GetName,
GetType,
GetAnnotations,
Is,
Sub,
Iter,
Length,
Member,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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


Expand Down
2 changes: 1 addition & 1 deletion typemap/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,4 @@ def IsSubSimilar(self, tps):
return _IsGenericAlias(self, tps)


Is = IsSubSimilar
Sub = IsSubSimilar