mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-22 21:25:13 +00:00
Don't let PyInstanceMethod hide itself
Python 3's `PyInstanceMethod_Type` hides itself via its `tp_descr_get`, which prevents aliasing methods via `cls.attr("m2") = cls.attr("m1")`: instead the `tp_descr_get` returns a plain function, when called on a class, or a `PyMethod`, when called on an instance. Override that behaviour for pybind11 types with a special bypass for `PyInstanceMethod_Types`.
This commit is contained in:
parent
a7f704b39b
commit
0a90b2db71
@ -149,6 +149,24 @@ extern "C" inline int pybind11_meta_setattro(PyObject* obj, PyObject* name, PyOb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PY_MAJOR_VERSION >= 3
|
||||||
|
/** Python 3's PyInstanceMethod_Type hides itself via its tp_descr_get, which prevents aliasing
|
||||||
|
* methods via cls.attr("m2") = cls.attr("m1"): instead the tp_descr_get returns a plain function,
|
||||||
|
* when called on a class, or a PyMethod, when called on an instance. Override that behaviour here
|
||||||
|
* to do a special case bypass for PyInstanceMethod_Types.
|
||||||
|
*/
|
||||||
|
extern "C" inline PyObject *pybind11_meta_getattro(PyObject *obj, PyObject *name) {
|
||||||
|
PyObject *descr = _PyType_Lookup((PyTypeObject *) obj, name);
|
||||||
|
if (descr && PyInstanceMethod_Check(descr)) {
|
||||||
|
Py_INCREF(descr);
|
||||||
|
return descr;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return PyType_Type.tp_getattro(obj, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/** This metaclass is assigned by default to all pybind11 types and is required in order
|
/** This metaclass is assigned by default to all pybind11 types and is required in order
|
||||||
for static properties to function correctly. Users may override this using `py::metaclass`.
|
for static properties to function correctly. Users may override this using `py::metaclass`.
|
||||||
Return value: New reference. */
|
Return value: New reference. */
|
||||||
@ -176,6 +194,9 @@ inline PyTypeObject* make_default_metaclass() {
|
|||||||
|
|
||||||
type->tp_new = pybind11_meta_new;
|
type->tp_new = pybind11_meta_new;
|
||||||
type->tp_setattro = pybind11_meta_setattro;
|
type->tp_setattro = pybind11_meta_setattro;
|
||||||
|
#if PY_MAJOR_VERSION >= 3
|
||||||
|
type->tp_getattro = pybind11_meta_getattro;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (PyType_Ready(type) < 0)
|
if (PyType_Ready(type) < 0)
|
||||||
pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!");
|
pybind11_fail("make_default_metaclass(): failure in PyType_Ready()!");
|
||||||
|
@ -135,6 +135,8 @@
|
|||||||
|
|
||||||
#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions
|
#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions
|
||||||
#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr)
|
#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr)
|
||||||
|
#define PYBIND11_INSTANCE_METHOD_CHECK PyInstanceMethod_Check
|
||||||
|
#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyInstanceMethod_GET_FUNCTION
|
||||||
#define PYBIND11_BYTES_CHECK PyBytes_Check
|
#define PYBIND11_BYTES_CHECK PyBytes_Check
|
||||||
#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString
|
#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString
|
||||||
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize
|
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize
|
||||||
@ -153,6 +155,8 @@
|
|||||||
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name()
|
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name()
|
||||||
#else
|
#else
|
||||||
#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_)
|
#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_)
|
||||||
|
#define PYBIND11_INSTANCE_METHOD_CHECK PyMethod_Check
|
||||||
|
#define PYBIND11_INSTANCE_METHOD_GET_FUNCTION PyMethod_GET_FUNCTION
|
||||||
#define PYBIND11_BYTES_CHECK PyString_Check
|
#define PYBIND11_BYTES_CHECK PyString_Check
|
||||||
#define PYBIND11_BYTES_FROM_STRING PyString_FromString
|
#define PYBIND11_BYTES_FROM_STRING PyString_FromString
|
||||||
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize
|
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize
|
||||||
|
@ -268,10 +268,8 @@ protected:
|
|||||||
rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__");
|
rec->is_constructor = !strcmp(rec->name, "__init__") || !strcmp(rec->name, "__setstate__");
|
||||||
rec->nargs = (std::uint16_t) args;
|
rec->nargs = (std::uint16_t) args;
|
||||||
|
|
||||||
#if PY_MAJOR_VERSION < 3
|
if (rec->sibling && PYBIND11_INSTANCE_METHOD_CHECK(rec->sibling.ptr()))
|
||||||
if (rec->sibling && PyMethod_Check(rec->sibling.ptr()))
|
rec->sibling = PYBIND11_INSTANCE_METHOD_GET_FUNCTION(rec->sibling.ptr());
|
||||||
rec->sibling = PyMethod_GET_FUNCTION(rec->sibling.ptr());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
detail::function_record *chain = nullptr, *chain_start = rec;
|
detail::function_record *chain = nullptr, *chain_start = rec;
|
||||||
if (rec->sibling) {
|
if (rec->sibling) {
|
||||||
|
@ -163,8 +163,8 @@ public:
|
|||||||
class NotRegistered {};
|
class NotRegistered {};
|
||||||
|
|
||||||
test_initializer methods_and_attributes([](py::module &m) {
|
test_initializer methods_and_attributes([](py::module &m) {
|
||||||
py::class_<ExampleMandA>(m, "ExampleMandA")
|
py::class_<ExampleMandA> emna(m, "ExampleMandA");
|
||||||
.def(py::init<>())
|
emna.def(py::init<>())
|
||||||
.def(py::init<int>())
|
.def(py::init<int>())
|
||||||
.def(py::init<const ExampleMandA&>())
|
.def(py::init<const ExampleMandA&>())
|
||||||
.def("add1", &ExampleMandA::add1)
|
.def("add1", &ExampleMandA::add1)
|
||||||
@ -222,6 +222,9 @@ test_initializer methods_and_attributes([](py::module &m) {
|
|||||||
.def("__str__", &ExampleMandA::toString)
|
.def("__str__", &ExampleMandA::toString)
|
||||||
.def_readwrite("value", &ExampleMandA::value);
|
.def_readwrite("value", &ExampleMandA::value);
|
||||||
|
|
||||||
|
// Issue #443: can't call copied methods in Python 3
|
||||||
|
emna.attr("add2b") = emna.attr("add2");
|
||||||
|
|
||||||
py::class_<TestProperties>(m, "TestProperties")
|
py::class_<TestProperties>(m, "TestProperties")
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def_readonly("def_readonly", &TestProperties::value)
|
.def_readonly("def_readonly", &TestProperties::value)
|
||||||
|
@ -80,6 +80,24 @@ def test_properties():
|
|||||||
assert instance.def_property == 3
|
assert instance.def_property == 3
|
||||||
|
|
||||||
|
|
||||||
|
def test_copy_method():
|
||||||
|
"""Issue #443: calling copied methods fails in Python 3"""
|
||||||
|
from pybind11_tests import ExampleMandA
|
||||||
|
|
||||||
|
ExampleMandA.add2c = ExampleMandA.add2
|
||||||
|
ExampleMandA.add2d = ExampleMandA.add2b
|
||||||
|
a = ExampleMandA(123)
|
||||||
|
assert a.value == 123
|
||||||
|
a.add2(ExampleMandA(-100))
|
||||||
|
assert a.value == 23
|
||||||
|
a.add2b(ExampleMandA(20))
|
||||||
|
assert a.value == 43
|
||||||
|
a.add2c(ExampleMandA(6))
|
||||||
|
assert a.value == 49
|
||||||
|
a.add2d(ExampleMandA(-7))
|
||||||
|
assert a.value == 42
|
||||||
|
|
||||||
|
|
||||||
def test_static_properties():
|
def test_static_properties():
|
||||||
from pybind11_tests import TestProperties as Type
|
from pybind11_tests import TestProperties as Type
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user