feat: py::type::of<T>() and py::type::of(h)#2364
Conversation
There was a problem hiding this comment.
Damn, @henryiii, you beat me to it, again ;-)
+1 for adding this!
When considering this PR over the last year, I realized this only works for class_-registered types. So, given that we'll undoubtedly get lots of questions/issues about "why doesn't py::type<int>() not return the int type object?":
- Should we extend this? Yes, your documentation says it's for exposed types, so it's not strictly necessary. But I do wonder how many "bug" reports we're going to get. Plus, it would be a nice way of getting hold of the
inttype object? - Is there an easy way to do so? One possibility I can see would be to add this to
type_casterasstatic py::object type()(orstatic py::handle type()) and add the current implementation totype_caster_base? Not sure if this is a great idea, but it would a) be extensible, and b) be backwards compatible (since you'll get a compile error if thetype_casterdoesn't have a statictypefunction).
|
Thanks!!! I've had some code that, uh, dipped into the internals for a while now 😅
EDIT: Thinking on it more, given that I already intercept I am totes fine with this only dealing with registered types. Perhaps, then renaming it to something slightly more obtuse, like |
I like the |
|
Yeah, I think a fail fast is achievable. Perhaps with something like this in the function body? |
Except that we don't have that, I believe? So it'll be something like checking that a subclass of the default |
|
If we have it fail for now with non-user registered types, it could always be added later - changing a failure to a success is always allowed. :) |
Exactly! As long as we do it safely (at compile time), this would be perfect! |
681c849 to
fd8f6fc
Compare
|
This looks great, but would you mind adding an explicit discussion in the documentation (the auto-extracted docstrings IIRC do end up in the documentation as well, but rather hidden..) |
|
Waiting pending discussion on #2388 |
2174918 to
8a9238d
Compare
|
By the way, I'm open to ideas on how to get something like |
|
Filed #2486 ;) |
Right, but then for
That's an interesting suggestion, though, but I'm not sure. It's not really a type, right? Can you create an object using this?
But these do get mapped to the same Python type? I don't see the problem, here? There's a mismatch between C++ and Python, but you can still say "If you pass a C++ type
Agreed! Let's get the easy stuff in, just making sure it doesn't stop us from achieving the more complicated stuff :-) |
EricCousineau-TRI
left a comment
There was a problem hiding this comment.
Looks awesome!
Without verifying it could be done, I think you could have
Unless it was extremely easy, was not planning to add to this PR :) |
include/pybind11/pytypes.h
Outdated
| PYBIND11_OBJECT_COMMON(type, object, PyType_Check) | ||
|
|
||
| explicit type(handle h): type((PyObject*) Py_TYPE(h.ptr()), borrowed_t{}) {} | ||
| explicit type(object ob): type((PyObject*) Py_TYPE(ob.ptr()), borrowed_t{}) {} |
There was a problem hiding this comment.
This isn't necessary, I think? object should be a subclass of handle, and there's no extra refcounting going on?
There was a problem hiding this comment.
Oh, yes, some slicing, since it's not by reference. But there are precedents for that, I think?
There was a problem hiding this comment.
If you/we choose to keep the object overload, better to make it a const object &, then. It avoids an unnecessary refcount.
There was a problem hiding this comment.
Ah, no, PYBIND11_OBJECT already adds that overload.
pybind11/include/pybind11/pytypes.h
Lines 813 to 817 in cc982ac
How do we fix this such that it works in the intended way? When do we want py::type(...) to "cast" a py::object to a subclass, and when do we want it to actually call type(...)? Does this mean that @henryiii's original py::type::of is necessary, and that we can't have the py::type(obj) interface I've been dreaming of? :-(
There was a problem hiding this comment.
We use PYBIND11_OBJECT_COMMON instead of PYBIND11_OBJECT.
There was a problem hiding this comment.
(I'll try dropping this overload soon)
There was a problem hiding this comment.
Can this be a const handle &?
There was a problem hiding this comment.
We use
PYBIND11_OBJECT_COMMONinstead ofPYBIND11_OBJECT.
🤦 How did I miss that?
Can this be a
const handle &?
Probably, but I don't think it's necessary. handle really is a fancy and more beautiful way of writing PyObject *.
|
Sorry to open that discussion again (after first telling @henryiii to change it), but, I'm now realizing the following: If |
|
I think I preferred |
|
It looks like |
I worked on that here in #2349, but it got stalled behind the |
|
Good, then leave as is, then we can take advantage of that if it gets merged? |
I really liked |
Good for me. I'll try to pick that up again, soon'ish... |
|
Thanks everyone! |
Pybind11 2.6 introduces `pybind11::type::of<T>()` and `pybind11::type`, see pybind/pybind11#2364. We can use neither: `of<T>()` throws and `type` cannot represent `None`.
Pybind11 2.6 introduces `pybind11::type::of<T>()` and `pybind11::type`, see pybind/pybind11#2364. We can use neither: `of<T>()` throws and `type` cannot represent `None`.
This adds
py::type::of<T>(), which provides a way to get the Python type associated with a C++ type. Currently there are two possible ways to do this (pointed out by @YannickJadoul and @bstaletic on Gitter about a year ago, IIRC), but they require usage of internal structures or the detail namespace. This provides a natural, pythonic way to get the type object without reverting to internal details.This is very useful in templated code; for example, in boost-histogram, it is needed to get the templated storage from a templated histogram.
EDIT (@YannickJadoul):
Closes #1692