mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-25 14:45:12 +00:00
Add is_final to disallow inheritance from Python
- Not currently supported on PyPy
This commit is contained in:
parent
b14aeb7cfa
commit
0dfffcf257
@ -1042,6 +1042,32 @@ described trampoline:
|
|||||||
``.def("foo", static_cast<int (A::*)() const>(&Publicist::foo));``
|
``.def("foo", static_cast<int (A::*)() const>(&Publicist::foo));``
|
||||||
where ``int (A::*)() const`` is the type of ``A::foo``.
|
where ``int (A::*)() const`` is the type of ``A::foo``.
|
||||||
|
|
||||||
|
Binding final classes
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Some classes may not be appropriate to inherit from. In C++11, classes can
|
||||||
|
use the ``final`` specifier to ensure that a class cannot be inherited from.
|
||||||
|
The ``py::is_final`` attribute can be used to ensure that Python classes
|
||||||
|
cannot inherit from a specified type. The underlying C++ type does not need
|
||||||
|
to be declared final.
|
||||||
|
|
||||||
|
.. code-block:: cpp
|
||||||
|
|
||||||
|
class IsFinal final {};
|
||||||
|
|
||||||
|
py::class_<IsFinal>(m, "IsFinal", py::is_final());
|
||||||
|
|
||||||
|
When you try to inherit from such a class in Python, you will now get this
|
||||||
|
error:
|
||||||
|
|
||||||
|
.. code-block:: pycon
|
||||||
|
|
||||||
|
>>> class PyFinalChild(IsFinal):
|
||||||
|
... pass
|
||||||
|
TypeError: type 'IsFinal' is not an acceptable base type
|
||||||
|
|
||||||
|
.. note:: This attribute is currently ignored on PyPy
|
||||||
|
|
||||||
Custom automatic downcasters
|
Custom automatic downcasters
|
||||||
============================
|
============================
|
||||||
|
|
||||||
|
@ -23,6 +23,9 @@ struct is_method { handle class_; is_method(const handle &c) : class_(c) { } };
|
|||||||
/// Annotation for operators
|
/// Annotation for operators
|
||||||
struct is_operator { };
|
struct is_operator { };
|
||||||
|
|
||||||
|
/// Annotation for classes that cannot be subclassed
|
||||||
|
struct is_final { };
|
||||||
|
|
||||||
/// Annotation for parent scope
|
/// Annotation for parent scope
|
||||||
struct scope { handle value; scope(const handle &s) : value(s) { } };
|
struct scope { handle value; scope(const handle &s) : value(s) { } };
|
||||||
|
|
||||||
@ -201,7 +204,7 @@ struct function_record {
|
|||||||
struct type_record {
|
struct type_record {
|
||||||
PYBIND11_NOINLINE type_record()
|
PYBIND11_NOINLINE type_record()
|
||||||
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false),
|
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false),
|
||||||
default_holder(true), module_local(false) { }
|
default_holder(true), module_local(false), is_final(false) { }
|
||||||
|
|
||||||
/// Handle to the parent scope
|
/// Handle to the parent scope
|
||||||
handle scope;
|
handle scope;
|
||||||
@ -254,6 +257,9 @@ struct type_record {
|
|||||||
/// Is the class definition local to the module shared object?
|
/// Is the class definition local to the module shared object?
|
||||||
bool module_local : 1;
|
bool module_local : 1;
|
||||||
|
|
||||||
|
/// Is the class inheritable from python classes?
|
||||||
|
bool is_final : 1;
|
||||||
|
|
||||||
PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) {
|
PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) {
|
||||||
auto base_info = detail::get_type_info(base, false);
|
auto base_info = detail::get_type_info(base, false);
|
||||||
if (!base_info) {
|
if (!base_info) {
|
||||||
@ -416,6 +422,11 @@ struct process_attribute<dynamic_attr> : process_attribute_default<dynamic_attr>
|
|||||||
static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; }
|
static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct process_attribute<is_final> : process_attribute_default<is_final> {
|
||||||
|
static void init(const is_final &, type_record *r) { r->is_final = true; }
|
||||||
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct process_attribute<buffer_protocol> : process_attribute_default<buffer_protocol> {
|
struct process_attribute<buffer_protocol> : process_attribute_default<buffer_protocol> {
|
||||||
static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; }
|
static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; }
|
||||||
|
@ -604,10 +604,12 @@ inline PyObject* make_new_python_type(const type_record &rec) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Flags */
|
/* Flags */
|
||||||
type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
|
type->tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE;
|
||||||
#if PY_MAJOR_VERSION < 3
|
#if PY_MAJOR_VERSION < 3
|
||||||
type->tp_flags |= Py_TPFLAGS_CHECKTYPES;
|
type->tp_flags |= Py_TPFLAGS_CHECKTYPES;
|
||||||
#endif
|
#endif
|
||||||
|
if (!rec.is_final)
|
||||||
|
type->tp_flags |= Py_TPFLAGS_BASETYPE;
|
||||||
|
|
||||||
if (rec.dynamic_attr)
|
if (rec.dynamic_attr)
|
||||||
enable_dynamic_attributes(heap_type);
|
enable_dynamic_attributes(heap_type);
|
||||||
|
@ -367,6 +367,14 @@ TEST_SUBMODULE(class_, m) {
|
|||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def("ptr", &Aligned::ptr);
|
.def("ptr", &Aligned::ptr);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// test_final
|
||||||
|
struct IsFinal final {};
|
||||||
|
py::class_<IsFinal>(m, "IsFinal", py::is_final());
|
||||||
|
|
||||||
|
// test_non_final_final
|
||||||
|
struct IsNonFinalFinal {};
|
||||||
|
py::class_<IsNonFinalFinal>(m, "IsNonFinalFinal", py::is_final());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int N> class BreaksBase { public: virtual ~BreaksBase() = default; };
|
template <int N> class BreaksBase { public: virtual ~BreaksBase() = default; };
|
||||||
|
@ -279,3 +279,21 @@ def test_aligned():
|
|||||||
if hasattr(m, "Aligned"):
|
if hasattr(m, "Aligned"):
|
||||||
p = m.Aligned().ptr()
|
p = m.Aligned().ptr()
|
||||||
assert p % 1024 == 0
|
assert p % 1024 == 0
|
||||||
|
|
||||||
|
|
||||||
|
# https://bitbucket.org/pypy/pypy/issues/2742
|
||||||
|
@pytest.unsupported_on_pypy
|
||||||
|
def test_final():
|
||||||
|
with pytest.raises(TypeError) as exc_info:
|
||||||
|
class PyFinalChild(m.IsFinal):
|
||||||
|
pass
|
||||||
|
assert str(exc_info.value).endswith("is not an acceptable base type")
|
||||||
|
|
||||||
|
|
||||||
|
# https://bitbucket.org/pypy/pypy/issues/2742
|
||||||
|
@pytest.unsupported_on_pypy
|
||||||
|
def test_non_final_final():
|
||||||
|
with pytest.raises(TypeError) as exc_info:
|
||||||
|
class PyNonFinalFinalChild(m.IsNonFinalFinal):
|
||||||
|
pass
|
||||||
|
assert str(exc_info.value).endswith("is not an acceptable base type")
|
||||||
|
Loading…
Reference in New Issue
Block a user