mypy icon indicating copy to clipboard operation
mypy copied to clipboard

stubgenc.py does not generate correct lines for staticmethods

Open EzioHelios opened this issue 3 years ago • 2 comments

Bug Report

When I use stubgen to generate stub file for a pybind11 module

klass
.def_static("some_static_method", &A::some_static_method,
    R"#(None)#", py::arg("a"), py::arg("b")
    );

where pybind11::class_ klass is bound to C++ class A

the result line is like:

class A:
    def some_static_method(self, *args, **kwargs): ...

which should be like:

class A:
    @static_method
    def some_static_method(a: T, b: T): ...

The issue here is related to a pybind11 discussion: [https://github.com/pybind/pybind11/issues/2403]. which says "a static method of class Foo, Foo.dict['f_static'] points to the static method decorator around the function object instead, not the function object itself"

So, I temporarily fixed this issue for my own project by appending a staticmethod checker right after the is_c_classmethod in file stubgenc.py:

def is_c_staticmethod(obj: object) -> bool:
    return type(obj).__name__ in ('staticmethod')
def generate_c_type_stub(module: ModuleType,
                         class_name: str,
                         obj: type,
                         output: List[str],
                         imports: List[str],
                         sigs: Optional[Dict[str, str]] = None,
                         class_sigs: Optional[Dict[str, str]] = None) -> None:
                ...
                if is_c_classmethod(value):
                    methods.append('@classmethod')
                    self_var = 'cls'
                elif is_c_staticmethod(value):
                    methods.append('@staticmethod')
                    value = value.__func__
                    self_var = None
                else:
                    self_var = 'self'
                ...

The result turns out correct with my pybind11 code. But I guess it may not work for other conditions?

EzioHelios avatar Sep 01 '22 03:09 EzioHelios

For overloaded static methods the @staticmethod decorator is added to the output only once at the top, because multiple overloads are inferred in value.__func__ and their stubs appended in a loop in function generate_c_function_stub:

class A:
    @staticmethod
    @overload
    def some_static_method(a: A1, b: B1): ...
    @overload
    def some_static_method(a: A2, b: B2): ...
    @overload
    def some_static_method(a: A3, b: B3): ...

Passing an additional decorator string argument to generate_c_function_stub, and use it in that loop can fix this problem:

class A:
    @overload
    @staticmethod
    def some_static_method(a: A1, b: B1): ...
    @overload
    @staticmethod
    def some_static_method(a: A2, b: B2): ...
    @overload
    @staticmethod
    def some_static_method(a: A3, b: B3): ...

EzioHelios avatar Sep 01 '22 07:09 EzioHelios

I created #14934 to fix this issue, but he pull request seems to have gone cold. I don't get any response from the reviewers and I can't merge it since I don't have the necessary permissions. How should I proceed? I would like to have this bug fixed.

WeilerMarcel avatar Jul 10 '23 16:07 WeilerMarcel