Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
gaoqiong
pybind11
Commits
0dfffcf2
Commit
0dfffcf2
authored
Apr 05, 2020
by
Dustin Spicuzza
Committed by
Wenzel Jakob
Apr 26, 2020
Browse files
Add is_final to disallow inheritance from Python
- Not currently supported on PyPy
parent
b14aeb7c
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
67 additions
and
2 deletions
+67
-2
docs/advanced/classes.rst
docs/advanced/classes.rst
+26
-0
include/pybind11/attr.h
include/pybind11/attr.h
+12
-1
include/pybind11/detail/class.h
include/pybind11/detail/class.h
+3
-1
tests/test_class.cpp
tests/test_class.cpp
+8
-0
tests/test_class.py
tests/test_class.py
+18
-0
No files found.
docs/advanced/classes.rst
View file @
0dfffcf2
...
@@ -1042,6 +1042,32 @@ described trampoline:
...
@@ -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
============================
============================
...
...
include/pybind11/attr.h
View file @
0dfffcf2
...
@@ -23,6 +23,9 @@ struct is_method { handle class_; is_method(const handle &c) : class_(c) { } };
...
@@ -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 {
...
@@ -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 {
...
@@ -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>
...
@@ -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
;
}
...
...
include/pybind11/detail/class.h
View file @
0dfffcf2
...
@@ -604,10 +604,12 @@ inline PyObject* make_new_python_type(const type_record &rec) {
...
@@ -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
);
...
...
tests/test_class.cpp
View file @
0dfffcf2
...
@@ -367,6 +367,14 @@ TEST_SUBMODULE(class_, m) {
...
@@ -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
;
};
...
...
tests/test_class.py
View file @
0dfffcf2
...
@@ -279,3 +279,21 @@ def test_aligned():
...
@@ -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"
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment