Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
pybind11
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
open
pybind11
Commits
0dfffcf2
Commit
0dfffcf2
authored
Apr 05, 2020
by
Dustin Spicuzza
Committed by
Wenzel Jakob
Apr 26, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add is_final to disallow inheritance from Python
- Not currently supported on PyPy
parent
b14aeb7c
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
67 additions
and
2 deletions
+67
-2
docs/advanced/classes.rst
+26
-0
include/pybind11/attr.h
+12
-1
include/pybind11/detail/class.h
+3
-1
tests/test_class.cpp
+8
-0
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
)
{
...
@@ -417,6 +423,11 @@ struct process_attribute<dynamic_attr> : process_attribute_default<dynamic_attr>
...
@@ -417,6 +423,11 @@ struct process_attribute<dynamic_attr> : process_attribute_default<dynamic_attr>
};
};
template
<>
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
>
{
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