@@ -708,6 +708,148 @@ def mixed(a: int, /, b: int) -> None: ...
708708static_assert(not is_subtype_of(CallableTypeFromFunction[variadic], CallableTypeFromFunction[mixed]))
709709```
710710
711+ #### Keyword-only
712+
713+ For keyword-only parameters, the name matters:
714+
715+ ``` py
716+ from knot_extensions import CallableTypeFromFunction, is_subtype_of, static_assert
717+
718+ def keyword_int (* , a : int ) -> None : ...
719+ def keyword_float (* , a : float ) -> None : ...
720+ def keyword_b (* , b : int ) -> None : ...
721+
722+ static_assert(is_subtype_of(CallableTypeFromFunction[keyword_float], CallableTypeFromFunction[keyword_int]))
723+ static_assert(not is_subtype_of(CallableTypeFromFunction[keyword_int], CallableTypeFromFunction[keyword_float]))
724+ static_assert(not is_subtype_of(CallableTypeFromFunction[keyword_int], CallableTypeFromFunction[keyword_b]))
725+ ```
726+
727+ But, the order of the keyword-only parameters does not:
728+
729+ ``` py
730+ def keyword_ab (* , a : float , b : float ) -> None : ...
731+ def keyword_ba (* , b : int , a : int ) -> None : ...
732+
733+ static_assert(is_subtype_of(CallableTypeFromFunction[keyword_ab], CallableTypeFromFunction[keyword_ba]))
734+ static_assert(not is_subtype_of(CallableTypeFromFunction[keyword_ba], CallableTypeFromFunction[keyword_ab]))
735+ ```
736+
737+ #### Keyword-only with default
738+
739+ ``` py
740+ from knot_extensions import CallableTypeFromFunction, is_subtype_of, static_assert
741+
742+ def float_with_default (* , a : float = 1 ) -> None : ...
743+ def int_with_default (* , a : int = 1 ) -> None : ...
744+ def int_keyword (* , a : int ) -> None : ...
745+ def empty () -> None : ...
746+
747+ static_assert(is_subtype_of(CallableTypeFromFunction[float_with_default], CallableTypeFromFunction[int_with_default]))
748+ static_assert(not is_subtype_of(CallableTypeFromFunction[int_with_default], CallableTypeFromFunction[float_with_default]))
749+
750+ static_assert(is_subtype_of(CallableTypeFromFunction[int_with_default], CallableTypeFromFunction[int_keyword]))
751+ static_assert(not is_subtype_of(CallableTypeFromFunction[int_keyword], CallableTypeFromFunction[int_with_default]))
752+
753+ static_assert(is_subtype_of(CallableTypeFromFunction[int_with_default], CallableTypeFromFunction[empty]))
754+ static_assert(not is_subtype_of(CallableTypeFromFunction[empty], CallableTypeFromFunction[int_with_default]))
755+ ```
756+
757+ Here, we mix keyword-only parameter with and without the default value:
758+
759+ ``` py
760+ def mixed (* , b : int = 1 , a : int ) -> None : ...
761+
762+ static_assert(is_subtype_of(CallableTypeFromFunction[mixed], CallableTypeFromFunction[int_keyword]))
763+ static_assert(not is_subtype_of(CallableTypeFromFunction[int_keyword], CallableTypeFromFunction[mixed]))
764+ ```
765+
766+ #### Keyword-only with positional
767+
768+ ``` py
769+ from knot_extensions import CallableTypeFromFunction, is_subtype_of, static_assert
770+
771+ def keywords1 (* , a : int , b : int ) -> None : ...
772+ def standard (b : float , a : float ) -> None : ...
773+
774+ static_assert(is_subtype_of(CallableTypeFromFunction[standard], CallableTypeFromFunction[keywords1]))
775+ static_assert(not is_subtype_of(CallableTypeFromFunction[keywords1], CallableTypeFromFunction[standard]))
776+ ```
777+
778+ The subtype can include additional standard parameters as long as it has the default value:
779+
780+ ``` py
781+ def standard_with_default (b : float , a : float , c : float = 1 ) -> None : ...
782+ def standard_without_default (b : float , a : float , c : float ) -> None : ...
783+
784+ static_assert(is_subtype_of(CallableTypeFromFunction[standard_with_default], CallableTypeFromFunction[keywords1]))
785+ static_assert(not is_subtype_of(CallableTypeFromFunction[standard_without_default], CallableTypeFromFunction[keywords1]))
786+ ```
787+
788+ Here, we mix keyword-only parameters with standard parameters:
789+
790+ ``` py
791+ def keywords2 (* , a : int , c : int , b : int ) -> None : ...
792+ def mixed (b : float , a : float , * , c : float ) -> None : ...
793+
794+ static_assert(is_subtype_of(CallableTypeFromFunction[mixed], CallableTypeFromFunction[keywords2]))
795+ static_assert(not is_subtype_of(CallableTypeFromFunction[keywords2], CallableTypeFromFunction[mixed]))
796+ ```
797+
798+ But, we shouldn't consider any unmatched positional-only parameters:
799+
800+ ``` py
801+ def mixed_positional (b : float , / , a : float ) -> None : ...
802+
803+ static_assert(not is_subtype_of(CallableTypeFromFunction[mixed_positional], CallableTypeFromFunction[keywords1]))
804+ ```
805+
806+ #### Keyword-variadic
807+
808+ The name of the keyword-variadic parameter does not need to be the same in the subtype.
809+
810+ ``` py
811+ from knot_extensions import CallableTypeFromFunction, is_subtype_of, static_assert
812+
813+ def kwargs_float (** kwargs2 : float ) -> None : ...
814+ def kwargs_int (** kwargs1 : int ) -> None : ...
815+
816+ static_assert(is_subtype_of(CallableTypeFromFunction[kwargs_float], CallableTypeFromFunction[kwargs_int]))
817+ static_assert(not is_subtype_of(CallableTypeFromFunction[kwargs_int], CallableTypeFromFunction[kwargs_float]))
818+ ```
819+
820+ A variadic parameter can be omitted in the subtype:
821+
822+ ``` py
823+ def empty () -> None : ...
824+
825+ static_assert(is_subtype_of(CallableTypeFromFunction[kwargs_int], CallableTypeFromFunction[empty]))
826+ static_assert(not is_subtype_of(CallableTypeFromFunction[empty], CallableTypeFromFunction[kwargs_int]))
827+ ```
828+
829+ #### Keyword-variadic with keyword-only
830+
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.
833+
834+ ``` py
835+ from knot_extensions import CallableTypeFromFunction, is_subtype_of, static_assert
836+
837+ def kwargs (** kwargs : float ) -> None : ...
838+ def keyword_only (* , a : int , b : float ) -> None : ...
839+ def keyword_variadic (* , a : int , ** kwargs : int ) -> None : ...
840+
841+ static_assert(is_subtype_of(CallableTypeFromFunction[kwargs], CallableTypeFromFunction[keyword_only]))
842+ static_assert(is_subtype_of(CallableTypeFromFunction[kwargs], CallableTypeFromFunction[keyword_variadic]))
843+ ```
844+
845+ This is valid only for positional-only parameter, not any other parameter kind:
846+
847+ ``` py
848+ def mixed (a : int , * , b : int ) -> None : ...
849+
850+ static_assert(not is_subtype_of(CallableTypeFromFunction[kwargs], CallableTypeFromFunction[mixed]))
851+ ```
852+
711853#### Empty
712854
713855When the supertype has an empty list of parameters, then the subtype can have any kind of parameters
0 commit comments