@@ -503,10 +503,20 @@ static_assert(is_subtype_of(CallableTypeFromFunction[int_param], CallableTypeFro
503503static_assert(is_subtype_of(CallableTypeFromFunction[int_param_different_name], CallableTypeFromFunction[int_param]))
504504```
505505
506+ Multiple positional-only parameters are checked in order:
507+
508+ ``` py
509+ def multi_param1 (a : float , b : int , c : str , / ) -> None : ...
510+ def multi_param2 (b : int , c : bool , a : str , / ) -> None : ...
511+
512+ static_assert(is_subtype_of(CallableTypeFromFunction[multi_param1], CallableTypeFromFunction[multi_param2]))
513+ static_assert(not is_subtype_of(CallableTypeFromFunction[multi_param2], CallableTypeFromFunction[multi_param1]))
514+ ```
515+
506516#### Positional-only with default value
507517
508518If the parameter has a default value, it's treated as optional. This means that the parameter at the
509- corresponding position in the other function does not need to have a default value.
519+ corresponding position in the supertype does not need to have a default value.
510520
511521``` py
512522from knot_extensions import CallableTypeFromFunction, is_subtype_of, static_assert
@@ -522,7 +532,7 @@ static_assert(is_subtype_of(CallableTypeFromFunction[int_with_default], Callable
522532static_assert(not is_subtype_of(CallableTypeFromFunction[int_without_default], CallableTypeFromFunction[int_with_default]))
523533```
524534
525- As the parameter itself is optional, it can be omitted in the subtype :
535+ As the parameter itself is optional, it can be omitted in the supertype :
526536
527537``` py
528538def empty () -> None : ...
@@ -532,9 +542,18 @@ static_assert(not is_subtype_of(CallableTypeFromFunction[int_without_default], C
532542static_assert(not is_subtype_of(CallableTypeFromFunction[empty], CallableTypeFromFunction[int_with_default]))
533543```
534544
545+ The subtype can include as many positional-only parameters as long as they have the default value:
546+
547+ ``` py
548+ def multi_param (a : float = 1 , b : int = 2 , c : str = " 3" , / ) -> None : ...
549+
550+ static_assert(is_subtype_of(CallableTypeFromFunction[multi_param], CallableTypeFromFunction[empty]))
551+ static_assert(not is_subtype_of(CallableTypeFromFunction[empty], CallableTypeFromFunction[multi_param]))
552+ ```
553+
535554#### Positional-only with other kinds
536555
537- If a parameter is declared as positional-only, then the corresponding parameter in the subtype
556+ If a parameter is declared as positional-only, then the corresponding parameter in the supertype
538557cannot be any other parameter kind.
539558
540559``` py
@@ -543,21 +562,15 @@ from knot_extensions import CallableTypeFromFunction, is_subtype_of, static_asse
543562def positional_only (a : int , / ) -> None : ...
544563def standard (a : int ) -> None : ...
545564def keyword_only (* , a : int ) -> None : ...
546- def variadic (* args : int ) -> None : ...
547- def keyword_variadic (** kwargs : int ) -> None : ...
565+ def variadic (* a : int ) -> None : ...
566+ def keyword_variadic (** a : int ) -> None : ...
548567
549568static_assert(not is_subtype_of(CallableTypeFromFunction[positional_only], CallableTypeFromFunction[standard]))
550569static_assert(not is_subtype_of(CallableTypeFromFunction[positional_only], CallableTypeFromFunction[keyword_only]))
551570static_assert(not is_subtype_of(CallableTypeFromFunction[positional_only], CallableTypeFromFunction[variadic]))
552571static_assert(not is_subtype_of(CallableTypeFromFunction[positional_only], CallableTypeFromFunction[keyword_variadic]))
553572```
554573
555- But, a positional-only parameter can be a subtype of a standard parameter:
556-
557- ``` py
558- static_assert(is_subtype_of(CallableTypeFromFunction[standard], CallableTypeFromFunction[positional_only]))
559- ```
560-
561574#### Standard
562575
563576A standard parameter is either a positional or a keyword parameter.
@@ -601,22 +614,44 @@ static_assert(is_subtype_of(CallableTypeFromFunction[int_with_default], Callable
601614static_assert(not is_subtype_of(CallableTypeFromFunction[empty], CallableTypeFromFunction[int_with_default]))
602615```
603616
604- #### Standard with other kinds
617+ Multiple standard parameters are checked in order along with their names:
605618
606- If the corresponding parameter in the subtype is a keyword-only parameter, it behaves in the same
607- way. This is because keyword-only parameter is one of the kind of standard parameter.
619+ ``` py
620+ def multi_param1 (a : float , b : int , c : str ) -> None : ...
621+ def multi_param2 (a : int , b : bool , c : str ) -> None : ...
622+
623+ static_assert(is_subtype_of(CallableTypeFromFunction[multi_param1], CallableTypeFromFunction[multi_param2]))
624+ static_assert(not is_subtype_of(CallableTypeFromFunction[multi_param2], CallableTypeFromFunction[multi_param1]))
625+ ```
626+
627+ The subtype can include as many standard parameters as long as they have the default value:
628+
629+ ``` py
630+ def multi_param_default (a : float = 1 , b : int = 2 , c : str = " s" ) -> None : ...
631+
632+ static_assert(is_subtype_of(CallableTypeFromFunction[multi_param_default], CallableTypeFromFunction[empty]))
633+ static_assert(not is_subtype_of(CallableTypeFromFunction[empty], CallableTypeFromFunction[multi_param_default]))
634+ ```
635+
636+ #### Standard with keyword-only
637+
638+ A keyword-only parameter in the supertype can be substituted with the corresponding standard
639+ parameter in the subtype with the same name. This is because a standard parameter is more flexible
640+ than a keyword-only parameter.
608641
609642``` py
610643from knot_extensions import CallableTypeFromFunction, is_subtype_of, static_assert
611644
612645def standard_a (a : int ) -> None : ...
613646def keyword_b (* , b : int ) -> None : ...
614647
648+ # The name of the parameters are different
615649static_assert(not is_subtype_of(CallableTypeFromFunction[standard_a], CallableTypeFromFunction[keyword_b]))
616650
617651def standard_float (a : float ) -> None : ...
618652def keyword_int (* , a : int ) -> None : ...
619653
654+ # Here, the name of the parameters are the same
620655static_assert(is_subtype_of(CallableTypeFromFunction[standard_float], CallableTypeFromFunction[keyword_int]))
621656
622657def standard_with_default (a : int = 1 ) -> None : ...
@@ -627,15 +662,28 @@ static_assert(is_subtype_of(CallableTypeFromFunction[standard_with_default], Cal
627662static_assert(is_subtype_of(CallableTypeFromFunction[standard_with_default], CallableTypeFromFunction[empty]))
628663```
629664
630- And, the same is for positional-only parameter except that the names are not required to be the
631- same.
665+ The position of the keyword-only parameters does not matter:
666+
667+ ``` py
668+ def multi_standard (a : float , b : int , c : str ) -> None : ...
669+ def multi_keyword (* , b : bool , c : str , a : int ) -> None : ...
670+
671+ static_assert(is_subtype_of(CallableTypeFromFunction[multi_standard], CallableTypeFromFunction[multi_keyword]))
672+ ```
673+
674+ #### Standard with positional-only
675+
676+ A positional-only parameter in the supertype can be substituted with the corresponding standard
677+ parameter in the subtype at the same position. This is because a standard parameter is more flexible
678+ than a positional-only parameter.
632679
633680``` py
634681from knot_extensions import CallableTypeFromFunction, is_subtype_of, static_assert
635682
636683def standard_a (a : int ) -> None : ...
637684def positional_b (b : int , / ) -> None : ...
638685
686+ # The names are not important in this context
639687static_assert(is_subtype_of(CallableTypeFromFunction[standard_a], CallableTypeFromFunction[positional_b]))
640688
641689def standard_float (a : float ) -> None : ...
@@ -651,14 +699,33 @@ static_assert(is_subtype_of(CallableTypeFromFunction[standard_with_default], Cal
651699static_assert(is_subtype_of(CallableTypeFromFunction[standard_with_default], CallableTypeFromFunction[empty]))
652700```
653701
654- And, with other kinds of parameter:
702+ The position of the positional-only parameters matter:
703+
704+ ``` py
705+ def multi_standard (a : float , b : int , c : str ) -> None : ...
706+ def multi_positional1 (b : int , c : bool , a : str , / ) -> None : ...
707+
708+ # Here, the type of the parameter `a` makes the subtype relation invalid
709+ def multi_positional2 (b : int , a : float , c : str , / ) -> None : ...
710+
711+ static_assert(is_subtype_of(CallableTypeFromFunction[multi_standard], CallableTypeFromFunction[multi_positional1]))
712+ static_assert(not is_subtype_of(CallableTypeFromFunction[multi_standard], CallableTypeFromFunction[multi_positional2]))
713+ ```
714+
715+ #### Standard with variadic
716+
717+ A standard parameter in the supertype cannot be substituted with a variadic or keyword-variadic
718+ parameter in the subtype.
655719
656720``` py
657- def variadic (* args : int ) -> None : ...
658- def keyword_variadic (** kwargs : int ) -> None : ...
721+ from knot_extensions import CallableTypeFromFunction, is_subtype_of, static_assert
722+
723+ def standard (a : int ) -> None : ...
724+ def variadic (* a : int ) -> None : ...
725+ def keyword_variadic (** a : int ) -> None : ...
659726
660- static_assert(not is_subtype_of(CallableTypeFromFunction[standard_a ], CallableTypeFromFunction[variadic]))
661- static_assert(not is_subtype_of(CallableTypeFromFunction[standard_a ], CallableTypeFromFunction[keyword_variadic]))
727+ static_assert(not is_subtype_of(CallableTypeFromFunction[standard ], CallableTypeFromFunction[variadic]))
728+ static_assert(not is_subtype_of(CallableTypeFromFunction[standard ], CallableTypeFromFunction[keyword_variadic]))
662729```
663730
664731#### Variadic
@@ -675,7 +742,7 @@ static_assert(is_subtype_of(CallableTypeFromFunction[variadic_float], CallableTy
675742static_assert(not is_subtype_of(CallableTypeFromFunction[variadic_int], CallableTypeFromFunction[variadic_float]))
676743```
677744
678- A variadic parameter can be omitted in the subtype :
745+ The variadic parameter does not need to be present in the supertype :
679746
680747``` py
681748def empty () -> None : ...
@@ -692,25 +759,31 @@ supertype should be checked against the variadic parameter.
692759``` py
693760from knot_extensions import CallableTypeFromFunction, is_subtype_of, static_assert
694761
695- def variadic (* args : float ) -> None : ...
696- def positional_only (a : int , b : float , / ) -> None : ...
697- def positional_variadic (a : int , / , * args : int ) -> None : ...
762+ def variadic (a : int , / , * args : float ) -> None : ...
763+
764+ # Here, the parameter `b` and `c` are unmatched
765+ def positional_only (a : int , b : float , c : int , / ) -> None : ...
766+
767+ # Here, the parameter `b` is unmatched and there's also a variadic parameter
768+ def positional_variadic (a : int , b : float , / , * args : int ) -> None : ...
698769
699770static_assert(is_subtype_of(CallableTypeFromFunction[variadic], CallableTypeFromFunction[positional_only]))
700771static_assert(is_subtype_of(CallableTypeFromFunction[variadic], CallableTypeFromFunction[positional_variadic]))
701772```
702773
703- This is valid only for positional-only parameter , not any other parameter kind:
774+ This is valid only for positional-only parameters , not any other parameter kind:
704775
705776``` py
706- def mixed (a : int , / , b : int ) -> None : ...
777+ # Parameter 1 is matched with the one at the same position, parameter 2 is unmatched so uses the
778+ # variadic parameter but the standard parameter `c` remains and cannot be matched.
779+ def mixed (a : int , b : float , / , c : int ) -> None : ...
707780
708781static_assert(not is_subtype_of(CallableTypeFromFunction[variadic], CallableTypeFromFunction[mixed]))
709782```
710783
711784#### Keyword-only
712785
713- For keyword-only parameters, the name matters :
786+ For keyword-only parameters, the name should be the same :
714787
715788``` py
716789from knot_extensions import CallableTypeFromFunction, is_subtype_of, static_assert
@@ -724,7 +797,7 @@ static_assert(not is_subtype_of(CallableTypeFromFunction[keyword_int], CallableT
724797static_assert(not is_subtype_of(CallableTypeFromFunction[keyword_int], CallableTypeFromFunction[keyword_b]))
725798```
726799
727- But, the order of the keyword-only parameters does not:
800+ But, the order of the keyword-only parameters is not required to be the same :
728801
729802``` py
730803def keyword_ab (* , a : float , b : float ) -> None : ...
@@ -754,25 +827,27 @@ static_assert(is_subtype_of(CallableTypeFromFunction[int_with_default], Callable
754827static_assert(not is_subtype_of(CallableTypeFromFunction[empty], CallableTypeFromFunction[int_with_default]))
755828```
756829
757- Here, we mix keyword-only parameter with and without the default value:
830+ Keyword-only parameters with default values can be mixed with the ones without default values in any
831+ order:
758832
759833``` py
834+ # A keyword-only parameter with a default value follows the one without a default value (it's valid)
760835def mixed (* , b : int = 1 , a : int ) -> None : ...
761836
762837static_assert(is_subtype_of(CallableTypeFromFunction[mixed], CallableTypeFromFunction[int_keyword]))
763838static_assert(not is_subtype_of(CallableTypeFromFunction[int_keyword], CallableTypeFromFunction[mixed]))
764839```
765840
766- #### Keyword-only with positional
841+ #### Keyword-only with standard
767842
768843``` py
769844from knot_extensions import CallableTypeFromFunction, is_subtype_of, static_assert
770845
771846def keywords1 (* , a : int , b : int ) -> None : ...
772847def standard (b : float , a : float ) -> None : ...
773848
774- static_assert(is_subtype_of(CallableTypeFromFunction[standard], CallableTypeFromFunction[keywords1]))
775849static_assert(not is_subtype_of(CallableTypeFromFunction[keywords1], CallableTypeFromFunction[standard]))
850+ static_assert(is_subtype_of(CallableTypeFromFunction[standard], CallableTypeFromFunction[keywords1]))
776851```
777852
778853The subtype can include additional standard parameters as long as it has the default value:
@@ -781,8 +856,8 @@ The subtype can include additional standard parameters as long as it has the def
781856def standard_with_default (b : float , a : float , c : float = 1 ) -> None : ...
782857def standard_without_default (b : float , a : float , c : float ) -> None : ...
783858
784- static_assert(is_subtype_of(CallableTypeFromFunction[standard_with_default], CallableTypeFromFunction[keywords1]))
785859static_assert(not is_subtype_of(CallableTypeFromFunction[standard_without_default], CallableTypeFromFunction[keywords1]))
860+ static_assert(is_subtype_of(CallableTypeFromFunction[standard_with_default], CallableTypeFromFunction[keywords1]))
786861```
787862
788863Here, we mix keyword-only parameters with standard parameters:
@@ -791,16 +866,24 @@ Here, we mix keyword-only parameters with standard parameters:
791866def keywords2 (* , a : int , c : int , b : int ) -> None : ...
792867def mixed (b : float , a : float , * , c : float ) -> None : ...
793868
794- static_assert(is_subtype_of(CallableTypeFromFunction[mixed], CallableTypeFromFunction[keywords2]))
795869static_assert(not is_subtype_of(CallableTypeFromFunction[keywords2], CallableTypeFromFunction[mixed]))
870+ static_assert(is_subtype_of(CallableTypeFromFunction[mixed], CallableTypeFromFunction[keywords2]))
796871```
797872
798873But, we shouldn't consider any unmatched positional-only parameters:
799874
800875``` py
801- def mixed_positional (b : float , / , a : float ) -> None : ...
876+ def mixed_positional (b : float , / , a : float , * , c : float ) -> None : ...
877+
878+ static_assert(not is_subtype_of(CallableTypeFromFunction[mixed_positional], CallableTypeFromFunction[keywords2]))
879+ ```
880+
881+ But, an unmatched variadic parameter is still valid:
882+
883+ ``` py
884+ def mixed_variadic (* args : float , a : float , b : float , c : float , ** kwargs : float ) -> None : ...
802885
803- static_assert(not is_subtype_of(CallableTypeFromFunction[mixed_positional ], CallableTypeFromFunction[keywords1 ]))
886+ static_assert(is_subtype_of(CallableTypeFromFunction[mixed_variadic ], CallableTypeFromFunction[keywords2 ]))
804887```
805888
806889#### Keyword-variadic
@@ -828,26 +911,30 @@ static_assert(not is_subtype_of(CallableTypeFromFunction[empty], CallableTypeFro
828911
829912#### Keyword-variadic with keyword-only
830913
831- If the subtype has a variadic parameter then any unmatched positional -only parameter from the
832- supertype should be checked against the variadic parameter.
914+ If the subtype has a keyword- variadic parameter then any unmatched keyword -only parameter from the
915+ supertype should be checked against the keyword- variadic parameter.
833916
834917``` py
835918from knot_extensions import CallableTypeFromFunction, is_subtype_of, static_assert
836919
837920def kwargs (** kwargs : float ) -> None : ...
838- def keyword_only (* , a : int , b : float ) -> None : ...
921+ def keyword_only (* , a : int , b : float , c : bool ) -> None : ...
839922def keyword_variadic (* , a : int , ** kwargs : int ) -> None : ...
840923
841924static_assert(is_subtype_of(CallableTypeFromFunction[kwargs], CallableTypeFromFunction[keyword_only]))
842925static_assert(is_subtype_of(CallableTypeFromFunction[kwargs], CallableTypeFromFunction[keyword_variadic]))
843926```
844927
845- This is valid only for positional -only parameter , not any other parameter kind:
928+ This is valid only for keyword -only parameters , not any other parameter kind:
846929
847930``` py
848- def mixed (a : int , * , b : int ) -> None : ...
931+ def mixed1 (a : int , * , b : int ) -> None : ...
932+
933+ # Same as above but with the default value
934+ def mixed2 (a : int = 1 , * , b : int ) -> None : ...
849935
850- static_assert(not is_subtype_of(CallableTypeFromFunction[kwargs], CallableTypeFromFunction[mixed]))
936+ static_assert(not is_subtype_of(CallableTypeFromFunction[kwargs], CallableTypeFromFunction[mixed1]))
937+ static_assert(not is_subtype_of(CallableTypeFromFunction[kwargs], CallableTypeFromFunction[mixed2]))
851938```
852939
853940#### Empty
0 commit comments