python / python/mypy

Error: "Local variable "x" has inferred type None; add an annotation", while annotation seems to be provided.

Abierto
#14,967 1 comentario 2 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

bug topic-mypyc
Lenguaje dominante
Python
Estrellas
20.6k
Forks
3.3k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

Bug Report

(A clear and concise description of what the bug is.)
mypyc complains about error: Local variable "x" has inferred type None; add an annotation, while it seems to me, that the annotation is added. mypy doesn't complain about it at all.

To Reproduce

git clone ---depth 1 https://github.com/pola-rs/polars

cd polars/py-polars

$ mypyc polars/utils/_construction.py
polars/utils/_construction.py:1050: error: Local variable "orient" has inferred type None; add an annotation
polars/utils/_construction.py:1248: error: Local variable "schema" has inferred type None; add an annotation

$ mypy polars/utils/_construction.py
Success: no issues found in 1 source file
$ tail -n +1046 polars/utils/_construction.py | head
def numpy_to_pydf(
    data: np.ndarray[Any, Any],
    schema: SchemaDefinition | None = None,
    schema_overrides: SchemaDict | None = None,
    orient: Orientation | None = None,
    nan_to_null: bool = False,
) -> PyDataFrame:
    """Construct a PyDataFrame from a numpy ndarray."""
    shape = data.shape


$ tail -n +1246 polars/utils/_construction.py | head
def iterable_to_pydf(
    data: Iterable[Any],
    schema: SchemaDefinition | None = None,
    schema_overrides: SchemaDict | None = None,
    orient: Orientation | None = None,
    chunk_size: int | None = None,
    infer_schema_length: int | None = N_INFER_DEFAULT,
) -> PyDataFrame:
    """Construct a PyDataFrame from an iterable/generator."""
    original_schema = schema

The polars/utils/_construction.py:1050: error: Local variable "orient" has inferred type None; add an annotation can be "fixed" by not accessing orient in the else ValueError branch.

def numpy_to_pydf(
    data: np.ndarray[Any, Any],
    schema: SchemaDefinition | None = None,
    schema_overrides: SchemaDict | None = None,
    orient: Orientation | None = None,
    nan_to_null: bool = False,
) -> PyDataFrame:
    """Construct a PyDataFrame from a numpy ndarray."""
    shape = data.shape

    # Unpack columns
    if shape == (0,):
        n_columns = 0

    elif len(shape) == 1:
        n_columns = 1

    elif len(shape) == 2:
        # default convention
        # first axis is rows, second axis is columns
        if orient is None and schema is None:
            n_columns = shape[1]
            orient = "row"

        # Infer orientation if columns argument is given
        elif orient is None and schema is not None:
            if len(schema) == shape[0]:
                orient = "col"
                n_columns = shape[0]
            else:
                orient = "row"
                n_columns = shape[1]

        elif orient == "row":
            n_columns = shape[1]
        elif orient == "col":
            n_columns = shape[0]
        else:
            raise ValueError(
                # Only commenting out `{orient}`, seems to work.
                # f"orient must be one of {{'col', 'row', None}}, got {orient} instead."
                f"orient must be one of {{'col', 'row', None}},"  # got {orient} instead."
            )
    else:
        raise ValueError(
            "Cannot create DataFrame from numpy array with more than two dimensions."
        )

    if schema is not None and len(schema) != n_columns:
        raise ValueError("Dimensions of columns arg must match data dimensions.")

    column_names, schema_overrides = _unpack_schema(
        schema, schema_overrides=schema_overrides, n_expected=n_columns
    )

    # Convert data to series
    if shape == (0,):
        data_series = []

    elif len(shape) == 1:
        data_series = [
            pli.Series(
                name=column_names[0],
                values=data,
                dtype=schema_overrides.get(column_names[0]),
                nan_to_null=nan_to_null,
            )._s
        ]
    else:
        if orient == "row":
            data_series = [
                pli.Series(
                    name=column_names[i],
                    values=data[:, i],
                    dtype=schema_overrides.get(column_names[i]),
                    nan_to_null=nan_to_null,
                )._s
                for i in range(n_columns)
            ]
        else:
            data_series = [
                pli.Series(
                    name=column_names[i],
                    values=data[i],
                    dtype=schema_overrides.get(column_names[i]),
                    nan_to_null=nan_to_null,
                )._s
                for i in range(n_columns)
            ]

    data_series = _handle_columns_arg(data_series, columns=column_names)
    return PyDataFrame(data_series)

Other things didn't seem to work:

❯ diff -u polars/utils/_construction.py polars/utils/_construction.py2
--- polars/utils/_construction.py	2023-03-27 15:17:50.072747854 +0200
+++ polars/utils/_construction.py2	2023-03-27 16:24:24.728991744 +0200
@@ -1076,14 +1076,16 @@
                 orient = "row"
                 n_columns = shape[1]
 
-        elif orient == "row":
+        elif orient is not None and orient == "row":
             n_columns = shape[1]
-        elif orient == "col":
+        elif orient is not None and orient == "col":
             n_columns = shape[0]
         else:
+            #assert orient is not None
             raise ValueError(
 #                f"orient must be one of {{'col', 'row', None}}, got {orient} instead."
-                 f"orient must be one of {{'col', 'row', None}},"  # got {orient} instead."
+                f"orient must be one of {{'col', 'row', None}}, got {orient if orient is not None else 'None'} instead."
             )
     else:
         raise ValueError(
@@ -1111,6 +1113,7 @@
             )._s
         ]
     else:
+        assert orient is not None
         if orient == "row":
             data_series = [
                 pli.Series(

For the other function, I have no clue how to "solve" polars/utils/_construction.py:1248: error: Local variable "schema" has inferred type None; add an annotation:

def iterable_to_pydf(
    data: Iterable[Any],
    schema: SchemaDefinition | None = None,
    schema_overrides: SchemaDict | None = None,
    orient: Orientation | None = None,
    chunk_size: int | None = None,
    infer_schema_length: int | None = N_INFER_DEFAULT,
) -> PyDataFrame:
    """Construct a PyDataFrame from an iterable/generator."""
    original_schema = schema
    column_names: list[str] = []
    dtypes_by_idx: dict[int, PolarsDataType] = {}
    if schema is not None:
        column_names, schema_overrides = _unpack_schema(
            schema, schema_overrides=schema_overrides
        )
    elif schema_overrides:
        _, schema_overrides = _unpack_schema(schema, schema_overrides=schema_overrides)

    if not isinstance(data, Generator):
        data = iter(data)

    if orient == "col":
        if column_names and schema_overrides:
            dtypes_by_idx = {
                idx: schema_overrides.get(col, Unknown)
                for idx, col in enumerate(column_names)
            }

        return pli.DataFrame(
            {
                (column_names[idx] if column_names else f"column_{idx}"): pli.Series(
                    coldata, dtype=dtypes_by_idx.get(idx)
                )
                for idx, coldata in enumerate(data)
            }
        )._df

    def to_frame_chunk(values: list[Any], schema: SchemaDefinition | None) -> DataFrame:
        return pli.DataFrame(
            data=values,
            schema=schema,
            orient="row",
            infer_schema_length=infer_schema_length,
        )

    n_chunks = 0
    n_chunk_elems = 1_000_000

    if chunk_size:
        adaptive_chunk_size = chunk_size
    elif column_names:
        adaptive_chunk_size = n_chunk_elems // len(column_names)
    else:
        adaptive_chunk_size = None

    df: DataFrame = None  # type: ignore[assignment]
    chunk_size = max(
        (infer_schema_length or 0),
        (adaptive_chunk_size or 1000),
    )
    while True:
        values = list(islice(data, chunk_size))
        if not values:
            break
        frame_chunk = to_frame_chunk(values, original_schema)
        if df is None:
            df = frame_chunk
            if not original_schema:
                original_schema = list(df.schema.items())
            if chunk_size != adaptive_chunk_size:
                chunk_size = adaptive_chunk_size = n_chunk_elems // len(df.columns)
        else:
            df.vstack(frame_chunk, in_place=True)
            n_chunks += 1

    if df is None:
        df = to_frame_chunk([], original_schema)

    return (df.rechunk() if n_chunks > 0 else df)._df

Expected Behavior

Actual Behavior

$ mypyc polars/utils/_construction.py
polars/utils/_construction.py:1050: error: Local variable "orient" has inferred type None; add an annotation
polars/utils/_construction.py:1248: error: Local variable "schema" has inferred type None; add an annotation

Your Environment

  • Mypy version used: 1.1.1 and 1.3.0+dev.2e75cbaa8e5c0fb8aa3548d5c7c8ccfd00131057
  • Mypy command-line flags:
  • Mypy configuration options from mypy.ini (and other config files):
  • Python version used: 3.8.10

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

Reproduce el informe con mypyc y mypy contra polars/utils/_construction.py, centrándote en los puntos de entrada numpy_to_pydf e iterable_to_pydf. Rastrea cómo gestiona mypyc las variables locales opcionales anotadas y compara sus diagnósticos con los de mypy; se considera completado cuando los errores de falso positivo reportados ya no se producen y la comprobación de tipos válida sigue intacta.

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

Evaluación

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

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.