TYP: get_indexer
State: closed

@simonjayhawkins im at a loss on why im getting a mypy complaint:

```
error: Overloaded function signatures 1 and 2 overlap with incompatible return types  [misc]
```

AFAICT they should be compatible.  Am I missing something obvious?

Labels:
name: Typing
description: type annotations, mypy/pyright type checking

Comments:
@simonjayhawkins gentle ping
can you rebase
cc @WillAyd any idea whats going on here?  my confusion only deepens when i try to do the same thing for Index.join
rebased, still no idea whats going on with mypy
@MarcoGorelli #40197 makes me think you have a better grasp of `@overload` than i do.  can you tell what im doing wrong here?
Indeed, this seems to work:
```python
    @overload
    def _get_indexer_non_comparable(
        self, target: Index, method, unique: Literal[True] = ...
    ) -> np.ndarray:
        # returned ndarray is np.intp
        ...

    @overload
    def _get_indexer_non_comparable(
        self, target: Index, method, unique: Literal[False]
    ) -> tuple[np.ndarray, np.ndarray]:
        # both returned ndarrays are np.intp
        ...

    @overload
    def _get_indexer_non_comparable(
        self, target: Index, method, unique: bool = ...
    ) -> Union[tuple[np.ndarray, np.ndarray], np.ndarray]:
        ...
```
> Indeed, this seems to work:

It does, but AFAICT it isnt the 3rd `@overload` that is making a difference, its the fact that in the second one you wrote `unique: Literal[False]` instead of `unique: Literal[False] = ...`
> It does, but AFAICT it isnt the 3rd @overload that is making a difference, its the fact that in the second one you wrote unique: Literal[False] instead of unique: Literal[False] = ...

related: what if the argument for which we need to omit the `= ...` comes after another keyword-only argument?

update: it looks like just pretending in the overloading line that that previous argument is not keyword-only works.  but that feels very sketchy
> It does, but AFAICT it isnt the 3rd `@overload` that is making a difference, its the fact that in the second one you wrote `unique: Literal[False]` instead of `unique: Literal[False] = ...`

You need to omit `=...` for `Literal[False]` because the default is `Literal[True]`.

Otherwise, consider what happens if you don't do `_get_indexer_non_comparable(target, method)`. Which overload should mypy use? `Literal[False]=...` and `Literal[True]=...` both match, and their return types are incompatible, so mypy throws an error. On the other hand, if you type the overloads as `Literal[True]=...` and `Literal[False]`, then if you don't specify the value of `inplace`, then mypy will know to use the one which accepts defaults, i.e. the `Literal[True]` overload.

Check the [mypy docs](https://mypy.readthedocs.io/en/stable/more_types.html#type-checking-the-variants) on this:
```
mypy will detect and prohibit inherently unsafely overlapping overloads on a best-effort basis.
Two variants are considered unsafely overlapping when both of the following are true:
    - All of the arguments of the first variant are compatible with the second.
    - The return type of the first variant is not compatible with (e.g. is not a subtype of) the second.
```

> related: what if the argument for which we need to omit the = ... comes after another keyword-only argument?

See what I did for `set_axis` - you need to provide overloads (without `=...`) for each combination of keyword arguments which comes before the one you're overloading

----

In case you haven't already, see this open issue in mypy about this topic: https://github.com/python/mypy/issues/6580
Are any of the remaining comments actionable?
gentle ping @MarcoGorelli 
Thanks for explaining - thanks @jbrockmendel