<commit_before>from abc import ABCMeta
import inspect
from inspect import Parameter, Signature
from typing import Callable

import typing_utils


def ensure_compatible(
    super_callable: Callable,
    sub_callable: Callable,
) -> None:
    """Ensure that the signature of `sub_callable` is compatible with the signature of `super_callable`.

    Guarantees that any call to `super_callable` will work on `sub_callable` by checking the following criteria:

    1. The return type of `sub_callable` is a subtype of the return type of `super_callable`.
    2. All parameters of `super_callable` are present in `sub_callable`, unless `sub_callable`
       declares `*args` or `**kwargs`.
    3. All positional parameters of `super_callable` appear in the same order in `sub_callable`.
    4. All parameters of `super_callable` are a subtype of the corresponding parameters of `sub_callable`.
    5. All required parameters of `sub_callable` are present in `super_callable`, unless `super_callable`
       declares `*args` or `**kwargs`.

    :param super_callable: Function to check compatibility with.
    :param sub_callable: Function to check compatibility of.
    """
    super_sig = inspect.signature(super_callable)
    sub_sig = inspect.signature(sub_callable)

    ensure_return_type_compatibility(super_sig, sub_sig)
    ensure_all_args_defined_in_sub(super_sig, sub_sig)
    ensure_no_extra_args_in_sub(super_sig, sub_sig)


def ensure_all_args_defined_in_sub(super_sig, sub_sig):
    sub_has_var_args = any(
        p.kind == Parameter.VAR_POSITIONAL for p in sub_sig.parameters.values()
    )
    sub_has_var_kwargs = any(
        p.kind == Parameter.VAR_KEYWORD for p in sub_sig.parameters.values()
    )
    for super_index, (name, super_param) in enumerate(super_sig.parameters.items()):
        if not is_param_defined_in_sub(name, sub_has_var_args, sub_has_var_kwargs, sub_sig, super_param):
            raise TypeError(f"`{name}` is not present.")
        elif (
                name in sub_sig.parameters
                and super_param.kind != Parameter.VAR_POSITIONAL
                and super_param.kind != Parameter.VAR_KEYWORD
        ):
            sub_index = list(sub_sig.parameters.keys()).index(name)
            sub_param = sub_sig.parameters[name]

            if (
                    super_param.kind != sub_param.kind
                    and not (
                    super_param.kind == Parameter.POSITIONAL_ONLY
                    and sub_param.kind == Parameter.POSITIONAL_OR_KEYWORD
            )
                    and not (
                    super_param.kind == Parameter.KEYWORD_ONLY
                    and sub_param.kind == Parameter.POSITIONAL_OR_KEYWORD
            )
            ):
                raise TypeError(f"`{name}` is not `{super_param.kind.description}`")
            elif super_index != sub_index and super_param.kind != Parameter.KEYWORD_ONLY:
                raise TypeError(f"`{name}` is not parameter `{super_index}`")
            elif (
                    super_param.annotation != Parameter.empty
                    and sub_param.annotation != Parameter.empty
                    and not typing_utils.issubtype(super_param.annotation, sub_param.annotation)
            ):
                raise TypeError(
                    f"`{name} must be a supertype of `{super_param.annotation}`"
                )


def is_param_defined_in_sub(name, sub_has_var_args, sub_has_var_kwargs, sub_sig, super_param):
    return (
            name in sub_sig.parameters
            or (super_param.kind == Parameter.VAR_POSITIONAL and not sub_has_var_args)
            or (super_param.kind == Parameter.VAR_KEYWORD and not sub_has_var_kwargs)
            or (super_param.kind == Parameter.POSITIONAL_ONLY and not sub_has_var_args)
            or (
            super_param.kind == Parameter.POSITIONAL_OR_KEYWORD
            and sub_has_var_args
            and sub_has_var_kwargs
            )
            or (super_param.kind == Parameter.KEYWORD_ONLY and sub_has_var_kwargs)
    )


def ensure_no_extra_args_in_sub(super_sig, sub_sig):
    super_var_args = any(
        p.kind == Parameter.VAR_POSITIONAL for p in super_sig.parameters.values()
    )
    super_var_kwargs = any(
        p.kind == Parameter.VAR_KEYWORD for p in super_sig.parameters.values()
    )
    for name, sub_param in sub_sig.parameters.items():
        if (
                name not in super_sig.parameters
                and sub_param.default == Parameter.empty
                and sub_param.kind != Parameter.VAR_POSITIONAL
                and sub_param.kind != Parameter.VAR_KEYWORD
                and not (sub_param.kind == Parameter.KEYWORD_ONLY and super_var_kwargs)<commit_msg>fix *args<commit_after>from abc import ABCMeta
import inspect
from inspect import Parameter, Signature
from typing import Callable

import typing_utils


def ensure_compatible(
    super_callable: Callable,
    sub_callable: Callable,
) -> None:
    """Ensure that the signature of `sub_callable` is compatible with the signature of `super_callable`.

    Guarantees that any call to `super_callable` will work on `sub_callable` by checking the following criteria:

    1. The return type of `sub_callable` is a subtype of the return type of `super_callable`.
    2. All parameters of `super_callable` are present in `sub_callable`, unless `sub_callable`
       declares `*args` or `**kwargs`.
    3. All positional parameters of `super_callable` appear in the same order in `sub_callable`.
    4. All parameters of `super_callable` are a subtype of the corresponding parameters of `sub_callable`.
    5. All required parameters of `sub_callable` are present in `super_callable`, unless `super_callable`
       declares `*args` or `**kwargs`.

    :param super_callable: Function to check compatibility with.
    :param sub_callable: Function to check compatibility of.
    """
    super_sig = inspect.signature(super_callable)
    sub_sig = inspect.signature(sub_callable)

    ensure_return_type_compatibility(super_sig, sub_sig)
    ensure_all_args_defined_in_sub(super_sig, sub_sig)
    ensure_no_extra_args_in_sub(super_sig, sub_sig)


def ensure_all_args_defined_in_sub(super_sig, sub_sig):
    sub_has_var_args = any(
        p.kind == Parameter.VAR_POSITIONAL for p in sub_sig.parameters.values()
    )
    sub_has_var_kwargs = any(
        p.kind == Parameter.VAR_KEYWORD for p in sub_sig.parameters.values()
    )
    for super_index, (name, super_param) in enumerate(super_sig.parameters.items()):
        if not is_param_defined_in_sub(name, sub_has_var_args, sub_has_var_kwargs, sub_sig, super_param):
            raise TypeError(f"`{name}` is not present.")
        elif (
                name in sub_sig.parameters
                and super_param.kind != Parameter.VAR_POSITIONAL
                and super_param.kind != Parameter.VAR_KEYWORD
        ):
            sub_index = list(sub_sig.parameters.keys()).index(name)
            sub_param = sub_sig.parameters[name]

            if (
                    super_param.kind != sub_param.kind
                    and not (
                    super_param.kind == Parameter.POSITIONAL_ONLY
                    and sub_param.kind == Parameter.POSITIONAL_OR_KEYWORD
            )
                    and not (
                    super_param.kind == Parameter.KEYWORD_ONLY
                    and sub_param.kind == Parameter.POSITIONAL_OR_KEYWORD
            )
            ):
                raise TypeError(f"`{name}` is not `{super_param.kind.description}`")
            elif super_index != sub_index and super_param.kind != Parameter.KEYWORD_ONLY:
                raise TypeError(f"`{name}` is not parameter `{super_index}`")
            elif (
                    super_param.annotation != Parameter.empty
                    and sub_param.annotation != Parameter.empty
                    and not typing_utils.issubtype(super_param.annotation, sub_param.annotation)
            ):
                raise TypeError(
                    f"`{name} must be a supertype of `{super_param.annotation}`"
                )


def is_param_defined_in_sub(name, sub_has_var_args, sub_has_var_kwargs, sub_sig, super_param):
    return (
            name in sub_sig.parameters
            or (super_param.kind == Parameter.VAR_POSITIONAL and sub_has_var_args)
            or (super_param.kind == Parameter.VAR_KEYWORD and sub_has_var_kwargs)
            or (super_param.kind == Parameter.POSITIONAL_ONLY and not sub_has_var_args)
            or (
            super_param.kind == Parameter.POSITIONAL_OR_KEYWORD
            and sub_has_var_args
            and sub_has_var_kwargs
            )
            or (super_param.kind == Parameter.KEYWORD_ONLY and sub_has_var_kwargs)
    )


def ensure_no_extra_args_in_sub(super_sig, sub_sig):
    super_var_args = any(
        p.kind == Parameter.VAR_POSITIONAL for p in super_sig.parameters.values()
    )
    super_var_kwargs = any(
        p.kind == Parameter.VAR_KEYWORD for p in super_sig.parameters.values()
    )
    for name, sub_param in sub_sig.parameters.items():
        if (
                name not in super_sig.parameters
                and sub_param.default == Parameter.empty
                and sub_param.kind != Parameter.VAR_POSITIONAL
                and sub_param.kind != Parameter.VAR_KEYWORD
                and not (sub_param.kind == Parameter.KEYWORD_ONLY and super_var_kwargs)
{'fraction_non_alphanumeric': 0.07702932426689332, 'fraction_numerical': 0.0010624734381640458, 'mean_word_length': 2.5149365197908886, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4698652', 'n_tokens_mistral': 2743, 'n_tokens_neox': 2683, 'n_words': 672}