Error: "Local variable "x" has inferred type None; add an annotation", while annotation seems to be provided.
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 20.6k
- Forks
- 3.3k
- PR merge metrics
- PR metrics pending
Description
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.1and1.3.0+dev.2e75cbaa8e5c0fb8aa3548d5c7c8ccfd00131057 - Mypy command-line flags:
- Mypy configuration options from
mypy.ini(and other config files): - Python version used: 3.8.10
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the report with mypyc and mypy against polars/utils/_construction.py, focusing on the numpy_to_pydf and iterable_to_pydf entry points. Trace how mypyc handles the annotated optional locals and compare its diagnostics with mypy; done means the reported false-positive errors no longer occur while the valid type checking remains intact.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- compilers, devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100