mirror of
https://github.com/pybind/pybind11.git
synced 2024-11-21 20:55:11 +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));``
|
||||
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
|
||||
============================
|
||||
|
||||
|
@ -23,6 +23,9 @@ struct is_method { handle class_; is_method(const handle &c) : class_(c) { } };
|
||||
/// Annotation for operators
|
||||
struct is_operator { };
|
||||
|
||||
/// Annotation for classes that cannot be subclassed
|
||||
struct is_final { };
|
||||
|
||||
/// Annotation for parent scope
|
||||
struct scope { handle value; scope(const handle &s) : value(s) { } };
|
||||
|
||||
@ -201,7 +204,7 @@ struct function_record {
|
||||
struct type_record {
|
||||
PYBIND11_NOINLINE type_record()
|
||||
: 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 scope;
|
||||
@ -254,6 +257,9 @@ struct type_record {
|
||||
/// Is the class definition local to the module shared object?
|
||||
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 *)) {
|
||||
auto base_info = detail::get_type_info(base, false);
|
||||
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; }
|
||||
};
|
||||
|
||||
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 <>
|
||||
struct process_attribute<buffer_protocol> : process_attribute_default<buffer_protocol> {
|
||||
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
|
||||
|
||||
/* 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
|
||||
type->tp_flags |= Py_TPFLAGS_CHECKTYPES;
|
||||
#endif
|
||||
if (!rec.is_final)
|
||||
type->tp_flags |= Py_TPFLAGS_BASETYPE;
|
||||
|
||||
if (rec.dynamic_attr)
|
||||
enable_dynamic_attributes(heap_type);
|
||||
|
@ -367,6 +367,14 @@ TEST_SUBMODULE(class_, m) {
|
||||
.def(py::init<>())
|
||||
.def("ptr", &Aligned::ptr);
|
||||
#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; };
|
||||
|
@ -279,3 +279,21 @@ def test_aligned():
|
||||
if hasattr(m, "Aligned"):
|
||||
p = m.Aligned().ptr()
|
||||
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