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

Nicer API to pass py::capsule destructor #752

Merged
merged 7 commits into from
Mar 22, 2017

Conversation

wjakob
Copy link
Member

@wjakob wjakob commented Mar 20, 2017

The current way of specifying a destructor for py::capsule instances strikes me as quite inelegant (the Python C API leaks through, requiring the user to deal with PyCapsule_GetPointer). This PR changes the convention.

@virtuald
Copy link
Contributor

I'm already using it in a module deployed to pypi.. any chance you can add a macro to detect the API change? :(

@wjakob
Copy link
Member Author

wjakob commented Mar 20, 2017

If it becomes part of 2.1 or 2.2, you can of course #ifdef based on PYBIND11_VERSION_MAJOR and PYBIND11_VERSION_MINOR

Copy link
Member

@dean0x7d dean0x7d left a comment

Choose a reason for hiding this comment

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

Nice, this definitely also removes the ambiguity of whether to borrow or steal that PyObject*.

pybind11_fail("Could not allocate capsule object!");
}

explicit capsule(const void *value, void (*destructor)(void *)) {
Copy link
Member

Choose a reason for hiding this comment

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

explicit isn't needed here.

@@ -1004,10 +1004,27 @@ class capsule : public object {
PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact)
PYBIND11_DEPRECATED("Use reinterpret_borrow<capsule>() or reinterpret_steal<capsule>()")
capsule(PyObject *ptr, bool is_borrowed) : object(is_borrowed ? object(ptr, borrowed) : object(ptr, stolen)) { }
explicit capsule(const void *value, void (*destruct)(PyObject *) = nullptr)
Copy link
Member

Choose a reason for hiding this comment

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

Maybe just deprecate this constructor for backward compatibility? (Just without the = nullptr default and explicit).

Copy link
Member Author

Choose a reason for hiding this comment

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

good idea!

pybind11_fail("Could not allocate capsule object!");
}

explicit capsule(const void *value, void (*destructor)(void *)) {
Copy link
Member

Choose a reason for hiding this comment

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

I was thinking it would be nice to make this take a function with a (deduced) T * argument, with a reinterpret cast of the capsule pointer to the deduced type when invoking the destructor. Unfortunately I think that would require some special handling to accept a lambda, so might not be worthwhile.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's what I had implemented at first (with an extra closure object stored in the capsule context), but it struck me as a bit overkill.

Copy link
Member Author

Choose a reason for hiding this comment

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

(In any case, that sort of thing can still be added later on if there a good case for this functionality can be made. This PR is about preventing fallout from a problematic API that is about to be documented.)

@wjakob
Copy link
Member Author

wjakob commented Mar 20, 2017

Thanks @dean0x7d, I added a deprecated constructor now. I'll merge this into 2.1 unless there are objections.

@wjakob wjakob added this to the v2.1 milestone Mar 20, 2017
@jagerman
Copy link
Member

No objections.

@dean0x7d
Copy link
Member

dean0x7d commented Mar 20, 2017

I'm not sure how common the use case of #733 is -- would it warrant adding a dedicated constructor? E.g. explicit capsule(void (*destructor)()) where the "dummy" pointer would be handled internally.

@jagerman
Copy link
Member

I think it would be nice to have. I don't understand why Python imposes != NULL on the pointer in the first place, but I don't see why we should be subject to the same limitation.

@virtuald
Copy link
Contributor

Well, the only reason why #733 uses a capsule is because module destructors aren't exposed. If they were, then it would just be added to wherever the module docs are and wouldn't need to worry about capsules.

Of course, only Python 3 supports module destructors IIRC, so you'd need this hack anyways -- but maybe it could be done internally?

@jagerman
Copy link
Member

Module destructors, using this as a workaround for Python 2, seems like a useful addition, but can be added later (i.e. for 2.2).

@jagerman
Copy link
Member

This is the last 2.1 PR; I think it's good as is, but the dummy-pointer version might also be useful. (Though I think it can wait as well, if you prefer to merge without it).

@wjakob
Copy link
Member Author

wjakob commented Mar 22, 2017

I had the same thought and just added it.

wjakob added a commit to wjakob/pybind11 that referenced this pull request Mar 22, 2017
@wjakob
Copy link
Member Author

wjakob commented Mar 22, 2017

Added docs & the destructor-only capsules.

@wjakob
Copy link
Member Author

wjakob commented Mar 22, 2017

That's a pretty weird PyPy failure -- it looks like the pytest capture object is not working.

@jagerman
Copy link
Member

Rebase against current master: I just pushed a change to use pypy 5.7 instead of the 5.8 nightly in the travis-ci test, which fixes the PyPy build.

@wjakob
Copy link
Member Author

wjakob commented Mar 22, 2017

thanks -- done.

@jagerman
Copy link
Member

I just turned on this cool new travis-ci setting for pybind11 PR builds: https://blog.travis-ci.com/2017-03-22-introducing-auto-cancellation

@wjakob
Copy link
Member Author

wjakob commented Mar 22, 2017

Cool -- that's a useful feature indeed!

@jagerman
Copy link
Member

Hmm, that didn't fix the PyPy test failure. I wonder if you could work around it by not using a capture, perhaps by setting/checking a static variable?

@dean0x7d
Copy link
Member

Doesn't PyPy need a couple of GC calls after a del?

@wjakob
Copy link
Member Author

wjakob commented Mar 22, 2017

that's right -- I forgot about that.

a = m.return_capsule_with_destructor_2()
del a
pytest.gc_collect()
print(capture)
Copy link
Member

Choose a reason for hiding this comment

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

Leftover from debugging.

@wjakob
Copy link
Member Author

wjakob commented Mar 22, 2017

that cancel feature is already starting to pay off :)

@wjakob
Copy link
Member Author

wjakob commented Mar 22, 2017

Ok, that did it!

@wjakob wjakob merged commit b16421e into pybind:master Mar 22, 2017
@wjakob wjakob deleted the capsule_destructor branch March 22, 2017 21:04
@wjakob wjakob restored the capsule_destructor branch March 22, 2017 21:07
rwgk pushed a commit to rwgk/pybind11 that referenced this pull request Oct 12, 2022
Use `PyCapsule_Destructor` (part of the stable Python ABI) instead of spelling out the C `typedef`.

The deprecation message is misleading. Replace with a message pointing to another existing ctor.

Background: According to @wjacob the original motivation for deprecating the ctor (in PR pybind#752) was to hide Python C API details, but PR pybind#902 brought those back with a new ctor, it cannot be avoided. Having a `PyCapsule_Destructor` or a `void (*destructor)(void *)` are two separate and valid use cases.
rwgk pushed a commit to rwgk/pybind11 that referenced this pull request Oct 12, 2022
Use `PyCapsule_Destructor` (part of the stable Python ABI) instead of spelling out the C `typedef`.

The deprecation message is misleading. Replace with a message pointing to another existing ctor.

Background: According to @wjakob the original motivation for deprecating the ctor (in PR pybind#752) was to hide Python C API details, but PR pybind#902 brought those back with a new ctor, it cannot be avoided. Having a `PyCapsule_Destructor` or a `void (*destructor)(void *)` are two separate and valid use cases.
rwgk pushed a commit that referenced this pull request Oct 12, 2022
Use `PyCapsule_Destructor` (part of the stable Python ABI) instead of spelling out the C `typedef`.

The deprecation message is misleading. Replace with a message pointing to another existing ctor.

Background: According to @wjakob the original motivation for deprecating the ctor (in PR #752) was to hide Python C API details, but PR #902 brought those back with a new ctor, it cannot be avoided. Having a `PyCapsule_Destructor` or a `void (*destructor)(void *)` are two separate and valid use cases.
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.

4 participants