22
33from __future__ import annotations
44
5- import contextlib
65import sys
7- from typing import (
6+ from typing import ( # type: ignore[attr-defined]
87 TYPE_CHECKING ,
98 Any ,
109 Callable ,
1716 Type ,
1817 TypeVar ,
1918 Union ,
19+ _GenericAlias ,
20+ _remove_dups_flatten ,
2021 _SpecialForm ,
22+ _tp_cache ,
23+ _type_check ,
2124 cast ,
2225)
2326
2427import typing_extensions
25- from typing_extensions import Never , ParamSpec , TypeAlias , TypeGuard , TypeVarTuple
28+ from typing_extensions import Never , ParamSpec , Self , TypeAlias , TypeGuard , TypeVarTuple
2629
2730from basedtyping .runtime_only import OldUnionType
2831
29- if TYPE_CHECKING :
30- from typing_extensions import override
31- else :
32-
33- def override (arg , / ):
34- # TODO: Remove when typing_extensions is >= 4.4
35- with contextlib .suppress (AttributeError , TypeError ):
36- # Skip the attribute silently if it is not writable.
37- # AttributeError happens if the object has __slots__ or a
38- # read-only property, TypeError if it's a builtin class.
39- arg .__override__ = True
40- return arg
41-
42-
4332if not TYPE_CHECKING :
44- # TODO: remove the TYPE_CHECKING block once these are typed in basedtypeshed
45- from typing import _GenericAlias , _remove_dups_flatten , _tp_cache , _type_check
46-
4733 if sys .version_info >= (3 , 11 ):
4834 from typing import _collect_parameters
4935 else :
@@ -64,20 +50,47 @@ def override(arg, /):
6450 "issubform" ,
6551 "Untyped" ,
6652 "Intersection" ,
53+ "TypeForm" ,
6754)
6855
69- if not TYPE_CHECKING :
56+ if TYPE_CHECKING :
57+ _tp_cache_typed : Callable [[T ], T ]
58+ else :
59+ _tp_cache_typed = _tp_cache
60+
61+
62+ class _BasedSpecialForm (_SpecialForm , _root = True ): # type: ignore[misc]
63+ _name : str
64+
65+ def __init_subclass__ (cls , _root = False ): # noqa: FBT002
66+ super ().__init_subclass__ (_root = _root ) # type: ignore[call-arg]
67+
68+ def __init__ (self , * args : object , ** kwargs : object ):
69+ self .alias = kwargs .pop ("alias" , _BasedGenericAlias )
70+ super ().__init__ (* args , ** kwargs )
71+
72+ def __repr__ (self ) -> str :
73+ return "basedtyping." + self ._name
74+
75+ def __and__ (self , other : object ) -> object :
76+ return Intersection [self , other ]
7077
71- class _BasedSpecialForm (_SpecialForm , _root = True ):
72- def __repr__ (self ):
73- return "basedtyping." + self ._name
78+ def __rand__ (self , other : object ) -> object :
79+ return Intersection [other , self ]
7480
75- if sys .version_info < (3 , 9 ):
81+ if sys .version_info < (3 , 9 ):
7682
77- def __getitem__ (self , item ):
78- if self ._name == "Intersection" :
79- return _IntersectionGenericAlias (self , item )
80- return None
83+ @_tp_cache_typed
84+ def __getitem__ (self , item : object ) -> object :
85+ return self .alias (self , item ) # type: ignore[operator]
86+
87+
88+ class _BasedGenericAlias (_GenericAlias , _root = True ):
89+ def __and__ (self , other : object ) -> object :
90+ return Intersection [self , other ]
91+
92+ def __rand__ (self , other : object ) -> object :
93+ return Intersection [other , self ]
8194
8295
8396if TYPE_CHECKING :
@@ -202,7 +215,7 @@ def _raise_generics_not_reified(cls) -> NoReturn:
202215 f" to instantiate a reified class: { cls ._orig_type_vars } "
203216 )
204217
205- def _check_generics_reified (cls ) -> None :
218+ def _check_generics_reified (cls ):
206219 if not cls ._generics_are_reified () or cls ._has_non_reified_type_vars ():
207220 cls ._raise_generics_not_reified ()
208221
@@ -221,7 +234,6 @@ def _is_subclass(cls, subclass: object) -> TypeGuard[_ReifiedGenericMetaclass]:
221234 cast (_ReifiedGenericMetaclass , subclass )._orig_class (),
222235 )
223236
224- @override
225237 def __subclasscheck__ (cls , subclass : object ) -> bool :
226238 if not cls ._is_subclass (subclass ):
227239 return False
@@ -241,7 +253,6 @@ def __subclasscheck__(cls, subclass: object) -> bool:
241253 subclass ._check_generics_reified ()
242254 return cls ._type_var_check (subclass .__reified_generics__ )
243255
244- @override
245256 def __instancecheck__ (cls , instance : object ) -> bool :
246257 if not cls ._is_subclass (type (instance )):
247258 return False
@@ -252,7 +263,6 @@ def __instancecheck__(cls, instance: object) -> bool:
252263 )
253264
254265 # need the generic here for pyright. see https://github.com/microsoft/pyright/issues/5488
255- @override
256266 def __call__ (cls : type [T ], * args : object , ** kwargs : object ) -> T :
257267 """A placeholder ``__call__`` method that gets called when the class is
258268 instantiated directly, instead of first supplying the type parameters.
@@ -317,7 +327,7 @@ class ReifiedGeneric(Generic[T], metaclass=_ReifiedGenericMetaclass):
317327 __type_vars__ : tuple [TypeVar , ...]
318328 """``TypeVar``\\ s that have not yet been reified. so this Tuple should always be empty by the time the ``ReifiedGeneric`` is instantiated"""
319329
320- @_tp_cache # type: ignore[name-defined , misc]
330+ @_tp_cache # type: ignore[no-any-expr , misc]
321331 def __class_getitem__ ( # type: ignore[no-any-decorated]
322332 cls , item : GenericItems
323333 ) -> type [ReifiedGeneric [T ]]:
@@ -375,8 +385,7 @@ def __class_getitem__( # type: ignore[no-any-decorated]
375385 ReifiedGenericCopy ._can_do_instance_and_subclass_checks_without_generics = False
376386 return ReifiedGenericCopy
377387
378- @override
379- def __init_subclass__ (cls ) -> None :
388+ def __init_subclass__ (cls ):
380389 cls ._can_do_instance_and_subclass_checks_without_generics = True
381390 super ().__init_subclass__ ()
382391
@@ -435,16 +444,17 @@ def issubform(form: _Forms, forminfo: _Forms) -> bool:
435444 Untyped : TypeAlias = Any # type: ignore[no-any-explicit]
436445elif sys .version_info >= (3 , 9 ):
437446
438- @_SpecialForm # `_SpecialForm`s init isn't typed
439- def Untyped (self : _SpecialForm , parameters : object ) -> NoReturn : # noqa: ARG001
447+ @_BasedSpecialForm
448+ def Untyped (
449+ self : _BasedSpecialForm , parameters : object # noqa: ARG001
450+ ) -> NoReturn :
440451 """Special type indicating that something isn't typed.
441452
442453 This is more specialized than ``Any`` and can help with gradually typing modules.
443454 """
444455 raise TypeError (f"{ self } is not subscriptable" )
445456
446457else :
447- # old version had the doc argument
448458 Untyped : Final = _BasedSpecialForm (
449459 "Untyped" ,
450460 doc = (
@@ -453,75 +463,96 @@ def Untyped(self: _SpecialForm, parameters: object) -> NoReturn: # noqa: ARG001
453463 ),
454464 )
455465
456- if not TYPE_CHECKING :
457466
458- class _IntersectionGenericAlias (_GenericAlias , _root = True ):
459- def copy_with (self , args ):
460- return Intersection [args ]
467+ class _IntersectionGenericAlias (_BasedGenericAlias , _root = True ):
468+ def copy_with (self , args : object ) -> Self : # type: ignore[override] # TODO: put in the overloads
469+ return cast ( Self , Intersection [args ])
461470
462- def __eq__ (self , other ) :
463- if not isinstance (other , _IntersectionGenericAlias ):
464- return NotImplemented
465- return set (self .__args__ ) == set (other .__args__ )
471+ def __eq__ (self , other : object ) -> bool :
472+ if not isinstance (other , _IntersectionGenericAlias ):
473+ return NotImplemented
474+ return set (self .__args__ ) == set (other .__args__ )
466475
467- def __hash__ (self ):
468- return hash (frozenset (self .__args__ ))
476+ def __hash__ (self ) -> int :
477+ return hash (frozenset (self .__args__ ))
469478
470- def __instancecheck__ (self , obj ) :
471- return self .__subclasscheck__ (type (obj ))
479+ def __instancecheck__ (self , obj : object ) -> bool :
480+ return self .__subclasscheck__ (type (obj ))
472481
473- def __subclasscheck__ (self , cls ) :
474- return any (issubclass (cls , arg ) for arg in self .__args__ )
482+ def __subclasscheck__ (self , cls : type [ object ]) -> bool :
483+ return all (issubclass (cls , arg ) for arg in self .__args__ )
475484
476- def __reduce__ (self ):
477- func , (_ , args ) = super ().__reduce__ ()
478- return func , (Intersection , args )
485+ def __reduce__ (self ) -> ( object , object ):
486+ func , (_ , args ) = super ().__reduce__ () # type: ignore[no-any-expr, misc]
487+ return func , (Intersection , args )
479488
480- if sys .version_info > (3 , 9 ):
481489
482- @_BasedSpecialForm
483- def Intersection (self , parameters ):
484- """Intersection type; Intersection[X, Y] means both X and Y.
490+ if sys .version_info > (3 , 9 ):
485491
486- To define an intersection:
487- - If using __future__.annotations, shortform can be used e.g. A & B
488- - otherwise the fullform must be used e.g. Intersection[A, B] .
492+ @ _BasedSpecialForm
493+ def Intersection ( self : _BasedSpecialForm , parameters : object ) -> object :
494+ """Intersection type; Intersection[X, Y] means both X and Y .
489495
490- Details:
491- - The arguments must be types and there must be at least one.
492- - None as an argument is a special case and is replaced by
493- type(None).
494- - Intersections of intersections are flattened, e.g.::
496+ To define an intersection:
497+ - If using __future__.annotations, shortform can be used e.g. A & B
498+ - otherwise the fullform must be used e.g. Intersection[A, B].
495499
496- Intersection[Intersection[int, str], float] == Intersection[int, str, float]
500+ Details:
501+ - The arguments must be types and there must be at least one.
502+ - None as an argument is a special case and is replaced by
503+ type(None).
504+ - Intersections of intersections are flattened, e.g.::
497505
498- - Intersections of a single argument vanish, e.g.::
506+ Intersection[Intersection[int, str], float] == Intersection[int, str, float]
499507
500- Intersection[int] == int # The constructor actually returns int
508+ - Intersections of a single argument vanish, e.g.::
501509
502- - Redundant arguments are skipped, e.g.::
510+ Intersection[int] == int # The constructor actually returns int
503511
504- Intersection[int, str, int] == Intersection[int, str]
512+ - Redundant arguments are skipped, e.g.::
505513
506- - When comparing intersections, the argument order is ignored, e.g.::
514+ Intersection[int, str, int] == Intersection[int, str]
507515
508- Intersection[int, str] == Intersection[str, int]
516+ - When comparing intersections, the argument order is ignored, e.g.::
509517
510- - You cannot subclass or instantiate an intersection.
511- """
512- if parameters == ():
513- raise TypeError ("Cannot take an Intersection of no types." )
514- if not isinstance (parameters , tuple ):
515- parameters = (parameters ,)
516- msg = "Intersection[arg, ...]: each arg must be a type."
517- parameters = tuple (_type_check (p , msg ) for p in parameters )
518- parameters = _remove_dups_flatten (parameters )
519- if len (parameters ) == 1 :
520- return parameters [0 ]
521- return _IntersectionGenericAlias (self , parameters )
518+ Intersection[int, str] == Intersection[str, int]
519+
520+ - You cannot subclass or instantiate an intersection.
521+ """
522+ if parameters == ():
523+ raise TypeError ("Cannot take an Intersection of no types." )
524+ if not isinstance (parameters , tuple ):
525+ parameters = (parameters ,)
526+ msg = "Intersection[arg, ...]: each arg must be a type."
527+ parameters = tuple (_type_check (p , msg ) for p in parameters ) # type: ignore[no-any-expr]
528+ parameters = _remove_dups_flatten (parameters ) # type: ignore[no-any-expr]
529+ if len (parameters ) == 1 : # type: ignore[no-any-expr]
530+ return parameters [0 ] # type: ignore[no-any-expr]
531+ return _IntersectionGenericAlias (self , parameters ) # type: ignore[arg-type, no-any-expr]
522532
523- else :
524- # old version had the doc argument
525- Intersection = _BasedSpecialForm ("Intersection" , doc = "" )
526533else :
527- Intersection : _SpecialForm
534+ Intersection = _BasedSpecialForm (
535+ "Intersection" , doc = "" , alias = _IntersectionGenericAlias
536+ )
537+
538+
539+ class _TypeFormForm (_BasedSpecialForm , _root = True ): # type: ignore[misc]
540+ def __init__ (self , doc : str ):
541+ self ._name = "TypeForm"
542+ self ._doc = self .__doc__ = doc
543+
544+ def __getitem__ (self , parameters : object | tuple [object ]) -> _BasedGenericAlias :
545+ if not isinstance (parameters , tuple ):
546+ parameters = (parameters ,)
547+
548+ return _BasedGenericAlias (self , parameters ) # type: ignore[arg-type]
549+
550+
551+ TypeForm = _TypeFormForm (doc = """\
552+ A type that can be used to represent a ``builtins.type`` or a ``SpecialForm``.
553+ For example:
554+
555+ def f[T](t: TypeForm[T]) -> T: ...
556+
557+ reveal_type(f(int | str)) # int | str
558+ """ )
0 commit comments