python / python/mypy

Collapsed Optionals in TypeVar cause weird error messages

Abierto
#8,478 2 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

bug false-positive priority-1-normal topic-type-variables
Lenguaje dominante
Python
Estrellas
20.6k
Forks
3.3k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

At a minimum, I'm reporting a bug in an error message.

I have a function, run_tasks, which takes a list of tasks and a worker function, and executes the worker function on almost all the tasks. For some of them, run_tasks will decide to not execute the task.

run_tasks returns List[Optional[Result]] where Result is a TypeVar.

This works until the worker function's returns Optional[x] where Result is now Optional[x] and run_tasks is asked to return List[Optional[Optional[x]]].

Example code:

from typing import (
    TypeVar,
    List,
    Callable,
    Optional,
)

Task = TypeVar("Task")
Result = TypeVar("Result")


def run_tasks(
    tasks: List[Task], worker_fun: Callable[[Task], Result]
) -> List[Optional[Result]]:
    results: List[Optional[Result]] = []

    toggle: bool = True
    todo: Task
    for todo in tasks:
        toggle = not toggle

        if toggle:
            work_result: Result
            work_result = worker_fun(todo)
            results.append(work_result)
        else:
            results.append(None)

    return results


def test() -> None:
    def worker(d: int) -> Optional[int]:
        if d % 2 == 0:
            return None
        else:
            return d + 1

    output: List[Optional[Optional[int]]] = run_tasks(
        [1, 2, 2, 1], worker,
    )
    from pprint import pprint

    pprint(output)
    assert output == [None, None, None, 2]


test()

I get the error message: error: Argument 2 to "run_tasks" has incompatible type "Callable[[int], Optional[int]]"; expected "Callable[[int], int]"

I understand Python can't really represent Optional[Optional[int]]. The problem is in the error message, I think. This code does work correctly, and validates:

def test2() -> None:
    def worker(d: int) -> int:
        return d + 1

    output: List[Optional[int]] = run_tasks(
        [1, 2, 2, 1], worker,
    )
    from pprint import pprint

    pprint(output)
    assert output == [None, 3, None, 2]


test2()

and so someone unfamiliar with the intricacies and details may be confused by the type signature being correctly updated in 2 places, but not validating.

Instead, I sort of expected it to either (a) work, or (b) have an error specific to the case of nested Optional.

$ mypy --version
mypy 0.761

$ python --version
Python 3.7.6

flags: none.

My apologies, but I did not test with mypy from Git master.

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Comienza ejecutando el reproductor proporcionado run_tasks con mypy y compara el diagnóstico de nested-Optional con el caso funcional test2. Se considera terminado cuando el error de Callable incompatible se haya corregido o reemplazado por un diagnóstico que explique con precisión este caso de nested Optional, con cobertura de regresión para el ejemplo.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
python
Área
devtools
Tipo de issue
Error
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
35/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.