Closed TimSchneider42 closed 10 months ago
This should give you a good place to start. It has issues from what I tested on if you use a param that is an std::variant as can be seen with example1
from pybind11_stubgen import *
from pybind11_stubgen.structs import *
class FixImplicitConversions(IParser):
def parse_annotation_str(
self, annotation_str: str
) -> ResolvedType | InvalidExpression | Value:
result = super().parse_annotation_str(annotation_str)
if isinstance(result, ResolvedType):
result: ResolvedType
if len(result.name) == 1 and result.parameters is None and result.name[0] == 'UtStringId':
result.name= QualifiedName.from_str('typing.Union')
result.parameters=[
ResolvedType(QualifiedName.from_str('str')),
ResolvedType(QualifiedName.from_str('UtStringId'))]
print(f'{result.__str__()} {annotation_str=}')
return result
def main():
logging.basicConfig(
level=logging.INFO,
format="%(name)s - [%(levelname)7s] %(message)s",
)
args = arg_parser().parse_args()
parser = stub_parser_from_args(args)
cls = parser.__class__
parser.__class__ = cls.__class__(cls.__name__ + "WithExtraBase", (FixImplicitConversions, cls), {})
printer = Printer(invalid_expr_as_ellipses=not args.print_invalid_expressions_as_is)
out_dir, sub_dir = to_output_and_subdir(
output_dir=args.output_dir,
module_name=args.module_name,
root_suffix=args.root_suffix,
)
run(
parser,
printer,
args.module_name,
out_dir,
sub_dir=sub_dir,
dry_run=args.dry_run,
writer=Writer(stub_ext=args.stub_extension),
)
if __name__ == '__main__':
main()
m.def("Example1", [](std::variant<std::string, UtStringId> value)
{
});
m.def("Example2", [](UtStringId& value)
{
});
m.def("Example3", [](std::vector<UtStringId> value)
{
});
m.def("Example4", [](std::vector<UtStringId> value)
{
return UtStringId();
});
m.def("Example5", [](WsfStringId value)
{
return WsfStringId();
})
def Example1(arg0: str | str | UtStringId) -> None:
...
def Example2(arg0: str | UtStringId) -> None:
...
def Example3(arg0: list[str | UtStringId]) -> None:
...
def Example4(arg0: list[str | UtStringId]) -> str | UtStringId:
...
def Example5(arg0: str | UtStringId) -> str | UtStringId:
...
@sizmailov it would be useful to have a way to dynamically add custom IParser impls to the default Parser similar to how you are dynamically adding the top and bottom error handlers. Also what is the reason behind the main Parser using inheritance instead of just being a list of parsers underneath the hood. multi inheritance seems like a much more rigid way of doing things.
@battleguard, thanks a lot! I will try it out once I find some time. I agree that providing a simpler way of adding custom IParser implementations would be useful.
Hey, one thing I just noticed is that the above solution also modifies the return types of functions. However, this I do not want. Is there any way I can ensure that only function input parameters are modified?
Edit: to be clear, I think my previous solution also suffered from this issue. I just didn't realize it until now.
@TimSchneider42 The solution to your problem could be close to what @battleguard suggested. Alternatively, you can manually transverse the resulting structure and patch it after the parsing stage.
@battleguard I agree. Currently, customization is far from elegant and requires repetition of __init__.py
. I suggest copying the whole __init__.py
to be safe from sudden changes since I didn't make __init__.py
robust to changes.
I've created #156 to address a more generic question. So I'll close this one.
Hi,
in my code I am using pybind11::implicitly_convertible to indicate that a class
A
can be implicitly converted toB
. Now whenever a function expects an argument of typeB
, I would like the type hint to beUnion[A, B]
.In version 0.16.0, I could achieve this with the following code:
However, in the latest version, this code does not work anymore. What would be the recommended way of solving this issue?
Best, Tim