| """Helpers for interacting with type var tuples.""" |
| |
| from __future__ import annotations |
| |
| from typing import Sequence |
| |
| from mypy.nodes import ARG_POS, ARG_STAR |
| from mypy.types import ( |
| CallableType, |
| Instance, |
| ProperType, |
| Type, |
| UnpackType, |
| get_proper_type, |
| split_with_prefix_and_suffix, |
| ) |
| |
| |
| def find_unpack_in_list(items: Sequence[Type]) -> int | None: |
| unpack_index: int | None = None |
| for i, item in enumerate(items): |
| if isinstance(item, UnpackType): |
| # We cannot fail here, so we must check this in an earlier |
| # semanal phase. |
| # Funky code here avoids mypyc narrowing the type of unpack_index. |
| old_index = unpack_index |
| assert old_index is None |
| # Don't return so that we can also sanity check there is only one. |
| unpack_index = i |
| return unpack_index |
| |
| |
| def split_with_instance( |
| typ: Instance, |
| ) -> tuple[tuple[Type, ...], tuple[Type, ...], tuple[Type, ...]]: |
| assert typ.type.type_var_tuple_prefix is not None |
| assert typ.type.type_var_tuple_suffix is not None |
| return split_with_prefix_and_suffix( |
| typ.args, typ.type.type_var_tuple_prefix, typ.type.type_var_tuple_suffix |
| ) |
| |
| |
| def split_with_mapped_and_template( |
| mapped: tuple[Type, ...], |
| mapped_prefix_len: int | None, |
| mapped_suffix_len: int | None, |
| template: tuple[Type, ...], |
| template_prefix_len: int, |
| template_suffix_len: int, |
| ) -> ( |
| tuple[ |
| tuple[Type, ...], |
| tuple[Type, ...], |
| tuple[Type, ...], |
| tuple[Type, ...], |
| tuple[Type, ...], |
| tuple[Type, ...], |
| ] |
| | None |
| ): |
| split_result = fully_split_with_mapped_and_template( |
| mapped, |
| mapped_prefix_len, |
| mapped_suffix_len, |
| template, |
| template_prefix_len, |
| template_suffix_len, |
| ) |
| if split_result is None: |
| return None |
| |
| ( |
| mapped_prefix, |
| mapped_middle_prefix, |
| mapped_middle_middle, |
| mapped_middle_suffix, |
| mapped_suffix, |
| template_prefix, |
| template_middle_prefix, |
| template_middle_middle, |
| template_middle_suffix, |
| template_suffix, |
| ) = split_result |
| |
| return ( |
| mapped_prefix + mapped_middle_prefix, |
| mapped_middle_middle, |
| mapped_middle_suffix + mapped_suffix, |
| template_prefix + template_middle_prefix, |
| template_middle_middle, |
| template_middle_suffix + template_suffix, |
| ) |
| |
| |
| def fully_split_with_mapped_and_template( |
| mapped: tuple[Type, ...], |
| mapped_prefix_len: int | None, |
| mapped_suffix_len: int | None, |
| template: tuple[Type, ...], |
| template_prefix_len: int, |
| template_suffix_len: int, |
| ) -> ( |
| tuple[ |
| tuple[Type, ...], |
| tuple[Type, ...], |
| tuple[Type, ...], |
| tuple[Type, ...], |
| tuple[Type, ...], |
| tuple[Type, ...], |
| tuple[Type, ...], |
| tuple[Type, ...], |
| tuple[Type, ...], |
| tuple[Type, ...], |
| ] |
| | None |
| ): |
| if mapped_prefix_len is not None: |
| assert mapped_suffix_len is not None |
| mapped_prefix, mapped_middle, mapped_suffix = split_with_prefix_and_suffix( |
| tuple(mapped), mapped_prefix_len, mapped_suffix_len |
| ) |
| else: |
| mapped_prefix = tuple() |
| mapped_suffix = tuple() |
| mapped_middle = mapped |
| |
| template_prefix, template_middle, template_suffix = split_with_prefix_and_suffix( |
| tuple(template), template_prefix_len, template_suffix_len |
| ) |
| |
| unpack_prefix = find_unpack_in_list(template_middle) |
| if unpack_prefix is None: |
| return ( |
| mapped_prefix, |
| (), |
| mapped_middle, |
| (), |
| mapped_suffix, |
| template_prefix, |
| (), |
| template_middle, |
| (), |
| template_suffix, |
| ) |
| |
| unpack_suffix = len(template_middle) - unpack_prefix - 1 |
| # mapped_middle is too short to do the unpack |
| if unpack_prefix + unpack_suffix > len(mapped_middle): |
| return None |
| |
| ( |
| mapped_middle_prefix, |
| mapped_middle_middle, |
| mapped_middle_suffix, |
| ) = split_with_prefix_and_suffix(mapped_middle, unpack_prefix, unpack_suffix) |
| ( |
| template_middle_prefix, |
| template_middle_middle, |
| template_middle_suffix, |
| ) = split_with_prefix_and_suffix(template_middle, unpack_prefix, unpack_suffix) |
| |
| return ( |
| mapped_prefix, |
| mapped_middle_prefix, |
| mapped_middle_middle, |
| mapped_middle_suffix, |
| mapped_suffix, |
| template_prefix, |
| template_middle_prefix, |
| template_middle_middle, |
| template_middle_suffix, |
| template_suffix, |
| ) |
| |
| |
| def extract_unpack(types: Sequence[Type]) -> ProperType | None: |
| """Given a list of types, extracts either a single type from an unpack, or returns None.""" |
| if len(types) == 1: |
| if isinstance(types[0], UnpackType): |
| return get_proper_type(types[0].type) |
| return None |
| |
| |
| def replace_starargs(callable: CallableType, types: list[Type]) -> CallableType: |
| star_index = callable.arg_kinds.index(ARG_STAR) |
| arg_kinds = ( |
| callable.arg_kinds[:star_index] |
| + [ARG_POS] * len(types) |
| + callable.arg_kinds[star_index + 1 :] |
| ) |
| arg_names = ( |
| callable.arg_names[:star_index] |
| + [None] * len(types) |
| + callable.arg_names[star_index + 1 :] |
| ) |
| arg_types = callable.arg_types[:star_index] + types + callable.arg_types[star_index + 1 :] |
| |
| return callable.copy_modified(arg_types=arg_types, arg_names=arg_names, arg_kinds=arg_kinds) |