Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow nanobind methods on non-nanobind classes. #104

Merged
merged 1 commit into from Dec 5, 2022

Conversation

hawkinsp
Copy link
Contributor

@hawkinsp hawkinsp commented Dec 2, 2022

This commit makes it possible to to retroactively add nanobind method bindings
to an existing Python class (previously, nanobind would fail with an error
message):

For example, this could look as follows in Python:

class MyClass:
    ...
MyClass.my_method = extension.my_method_impl

where my_method_impl might be defined in C++

m.attr("my_method_impl") = nb::cpp_function([](nb::object self, int x) {
    ...
}, nb::is_method());

This feature can particularly useful when MyClass was created using another
binding tool like pybind11. In this case, methods can be individually migrated
from one binding tool to the other over time.

@hawkinsp
Copy link
Contributor Author

hawkinsp commented Dec 2, 2022

Sigh — pypy fails again. I'm not sure what's going on here (I'll have to figure out how to install the very new PyPy you're requiring). I'll look into it, but assuming I can fix that, would you object to this change?

@hawkinsp
Copy link
Contributor Author

hawkinsp commented Dec 2, 2022

Sigh — pypy fails again. I'm not sure what's going on here (I'll have to figure out how to install the very new PyPy you're requiring). I'll look into it, but assuming I can fix that, would you object to this change?

The problem turned out to be a pre-existing bug in the dispatch of bound methods. When using the non-NB_VECTORCALL_ARGUMENTS_OFFSET path and copying the arguments, we need to copy the keyword arguments also which aren't counted in nargs.

Copy link
Owner

@wjakob wjakob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a nice improvement! One potential issue could arise when applying a constructor (__init__)-style function that has is_constructor set to true. In this case, the implementation would attempt to write into the PyObject * that has a different memory layout.

See: https://github.com/wjakob/nanobind/blob/master/src/nb_func.cpp#L637 and https://github.com/wjakob/nanobind/blob/master/src/nb_func.cpp#L766

My suggestion would be that is_constructor is set to false by default and only filled to its current value once we enter the branch for classes with the nb_type metaclass.

if (!args_tmp)
return PyErr_NoMemory();
args_tmp[0] = mb->self;
for (size_t i = 0; i < nargs; ++i)
for (size_t i = 0; i < nargs + nkwargs_in; ++i)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a great catch 👍

This allows us to extend an existing Python (or
pybind11!) class with methods defined in nanobind. This is handy if you
want to add a small amount of nanobind code to a class
defined in other ways. For example, one use is to migrate methods of a
pybind11-defined class to nanobind one by one.

All we have to do to allow this is remove the type check. I
can't see anything that goes wrong if we simply allow dispatch to
proceed.
@hawkinsp
Copy link
Contributor Author

hawkinsp commented Dec 5, 2022

This looks like a nice improvement! One potential issue could arise when applying a constructor (__init__)-style function that has is_constructor set to true. In this case, the implementation would attempt to write into the PyObject * that has a different memory layout.

See: https://github.com/wjakob/nanobind/blob/master/src/nb_func.cpp#L637 and https://github.com/wjakob/nanobind/blob/master/src/nb_func.cpp#L766

My suggestion would be that is_constructor is set to false by default and only filled to its current value once we enter the branch for classes with the nb_type metaclass.

Done!

@wjakob wjakob merged commit 3635c88 into wjakob:master Dec 5, 2022
@wjakob
Copy link
Owner

wjakob commented Dec 5, 2022

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants