Closed
Description
Bug Report
Trying to update mypy from 1.6.1 to 1.7.0 has revealed a few regressions in the stub generator output related to pybind11 modules.
To Reproduce
Consider this pybind11 module:
#include <filesystem>
#include <optional>
#include <utility>
#include <vector>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
namespace py = pybind11;
std::vector<float> funcReturningVector()
{
return std::vector<float>{1.0, 2.0, 3.0, 4.0};
}
std::pair<int, float> funcReturningPair()
{
return std::pair{42, 1.0};
}
std::optional<int> funcReturningOptional()
{
return std::nullopt;
}
std::filesystem::path funcIncompleteSignature()
{
// This example does not include <pybind11/stl/filesystem.h> on purpose
// to demonstrate the signature of an incomplete binding.
return std::filesystem::path{"foobar"};
}
struct MyStruct
{
int a;
int b;
int c;
};
PYBIND11_MODULE(my_native_module, m)
{
m.def("func_returning_vector", &funcReturningVector);
m.def("func_returning_pair", &funcReturningPair);
m.def("func_returning_optional", &funcReturningOptional);
m.def("func_incomplete_signature", &funcIncompleteSignature);
py::class_<MyStruct>(m, "MyStruct")
.def_readwrite("a", &MyStruct::a)
.def_readwrite("b", &MyStruct::b, "some docstring")
.def_property_readonly(
"c",
[](const MyStruct& x) {
return x.c;
},
"some docstring");
}
To reproduce, store the above snippet as my_native_module.cpp
and then:
# Install latest pybind11:
pip install pybind11==2.11.1
# Compile pybind11 module:
c++ -O3 -Wall -shared -std=c++17 -fPIC $(python3 -m pybind11 --includes) my_native_module.cpp -o my_native_module.so
# Run the stub generator on it:
stubgen -p my_native_module
Expected Behavior
mypy 1.6.1 outputs the following, which is pretty reasonable:
from typing import Any, List, Optional, Tuple
class MyStruct:
a: int
b: int
def __init__(self, *args, **kwargs) -> None: ...
@property
def c(self) -> int: ...
def func_incomplete_signature(*args, **kwargs) -> Any: ...
def func_returning_optional() -> Optional[int]: ...
def func_returning_pair() -> Tuple[int,float]: ...
def func_returning_vector() -> List[float]: ...
Actual Behavior
mypy 1.7.0 outputs the following:
from _typeshed import Incomplete
class MyStruct:
a: int
b: Incomplete
def __init__(self, *args, **kwargs) -> None: ...
@property
def c(self): ...
def func_incomplete_signature(*args, **kwargs): ...
def func_returning_optional() -> Optional[int]: ...
def func_returning_pair() -> Tuple[int, float]: ...
def func_returning_vector() -> List[float]: ...
The regressions are:
- The most obvious thing is that the imports for
List
,Tuple
, andOptional
are missing. This makes the resulting.pyi
invalid, because it uses symbols that have not been imported. The stub output does not type check. This is not limited to these three types, but applies to other types likeIterable
,Iterator
,Dict
,Set
,Union
, etc. - The type annotation of field
b
has regressed fromint
toIncomplete
, apparently due to the explicit docstring. - The type annotation of field
c
has regressed fromint
to not annotated at all, probably also because of the explicit docstring. - The signature of the
func_incomplete_signature
has also slightly worsened: Before, it was at least annotated as-> Any
but now it misses any return type annotation at all. This is problematic when users want to validate their generated stubs for completeness, i.e., runmypy --disallow-untyped-defs
on the output. Of course semantically it is the same, but still it would be nicer to always produce some return type (arguably a broken signature may be rather annotated as-> object
instead of-> Incomplete
to avoid running into bugs from incomplete stub output).
Your Environment
- Mypy version used: 1.7.0
- Mypy command-line flags: irrelevant for stubgen
- Mypy configuration options from
mypy.ini
(and other config files): irrelevant for stubgen - Python version used: 3.10