Skip to content

Commit aa5b557

Browse files
committed
Enable static properties by default and deprecate py::metaclass
Now that only one shared metaclass is ever allocated, it's extremely cheap to enable it for all pybind11 types.
1 parent 7f164c7 commit aa5b557

File tree

7 files changed

+24
-54
lines changed

7 files changed

+24
-54
lines changed

docs/advanced/classes.rst

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -437,24 +437,15 @@ The section on :ref:`properties` discussed the creation of instance properties
437437
that are implemented in terms of C++ getters and setters.
438438

439439
Static properties can also be created in a similar way to expose getters and
440-
setters of static class attributes. Two things are important to note:
441-
442-
1. Static properties are implemented by instrumenting the *metaclass* of the
443-
class in question -- however, this requires the class to have a modifiable
444-
metaclass in the first place. pybind11 provides a ``py::metaclass()``
445-
annotation that must be specified in the ``class_`` constructor, or any
446-
later method calls to ``def_{property_,∅}_{readwrite,readonly}_static`` will
447-
fail (see the example below).
448-
449-
2. For static properties defined in terms of setter and getter functions, note
450-
that the implicit ``self`` argument also exists in this case and is used to
451-
pass the Python ``type`` subclass instance. This parameter will often not be
452-
needed by the C++ side, and the following example illustrates how to
453-
instantiate a lambda getter function that ignores it:
440+
setters of static class attributes. Note that the implicit ``self`` argument
441+
also exists in this case and is used to pass the Python ``type`` subclass
442+
instance. This parameter will often not be needed by the C++ side, and the
443+
following example illustrates how to instantiate a lambda getter function
444+
that ignores it:
454445

455446
.. code-block:: cpp
456447
457-
py::class_<Foo>(m, "Foo", py::metaclass())
448+
py::class_<Foo>(m, "Foo")
458449
.def_property_readonly_static("foo", [](py::object /* self */) { return Foo(); });
459450
460451
Operator overloading

include/pybind11/attr.h

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,11 @@ struct dynamic_attr { };
5353
/// Annotation which enables the buffer protocol for a type
5454
struct buffer_protocol { };
5555

56-
/// Annotation which requests that a special metaclass is created for a type
57-
struct metaclass { };
56+
/// DEPRECATED: Annotation which requests that a special metaclass is created for a type
57+
struct metaclass {
58+
PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.")
59+
metaclass() = default;
60+
};
5861

5962
/// Annotation to mark enums as an arithmetic type
6063
struct arithmetic { };
@@ -149,8 +152,7 @@ struct function_record {
149152
/// Special data structure which (temporarily) holds metadata about a bound class
150153
struct type_record {
151154
PYBIND11_NOINLINE type_record()
152-
: multiple_inheritance(false), dynamic_attr(false),
153-
buffer_protocol(false), metaclass(false) { }
155+
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false) { }
154156

155157
/// Handle to the parent scope
156158
handle scope;
@@ -188,9 +190,6 @@ struct type_record {
188190
/// Does the class implement the buffer protocol?
189191
bool buffer_protocol : 1;
190192

191-
/// Does the class require its own metaclass?
192-
bool metaclass : 1;
193-
194193
/// Is the default (unique_ptr) holder type used?
195194
bool default_holder : 1;
196195

@@ -354,11 +353,8 @@ struct process_attribute<buffer_protocol> : process_attribute_default<buffer_pro
354353
static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; }
355354
};
356355

357-
template <>
358-
struct process_attribute<metaclass> : process_attribute_default<metaclass> {
359-
static void init(const metaclass &, type_record *r) { r->metaclass = true; }
360-
};
361-
356+
// DEPRECATED
357+
template <> struct process_attribute<metaclass> : process_attribute_default<metaclass> { };
362358

363359
/// Process an 'arithmetic' attribute for enums (does nothing here)
364360
template <>

include/pybind11/detail/class_.h

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,8 @@ inline PyObject *make_object_base_type(size_t instance_size) {
270270
issue no Python C API calls which could potentially invoke the
271271
garbage collector (the GC will call type_traverse(), which will in
272272
turn find the newly constructed type in an invalid state) */
273-
auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0);
273+
const auto &internals = get_internals();
274+
auto heap_type = (PyHeapTypeObject *) internals.metatype->tp_alloc(internals.metatype, 0);
274275
if (!heap_type)
275276
pybind11_fail("make_object_base_type(): error allocating type!");
276277

@@ -354,7 +355,7 @@ inline PyObject* make_new_python_type(const type_record &rec) {
354355
issue no Python C API calls which could potentially invoke the
355356
garbage collector (the GC will call type_traverse(), which will in
356357
turn find the newly constructed type in an invalid state) */
357-
auto heap_type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0);
358+
auto heap_type = (PyHeapTypeObject *) internals.metatype->tp_alloc(internals.metatype, 0);
358359
if (!heap_type)
359360
pybind11_fail(std::string(rec.name) + ": Unable to create type object!");
360361

@@ -371,12 +372,6 @@ inline PyObject* make_new_python_type(const type_record &rec) {
371372
if (bases.size() > 0)
372373
type->tp_bases = bases.release().ptr();
373374

374-
/* Custom metaclass if requested (used for static properties) */
375-
if (rec.metaclass) {
376-
Py_INCREF(internals.metatype);
377-
Py_TYPE(type) = (PyTypeObject *) internals.metatype;
378-
}
379-
380375
/* Supported protocols */
381376
type->tp_as_number = &heap_type->as_number;
382377
type->tp_as_sequence = &heap_type->as_sequence;

include/pybind11/pybind11.h

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -828,18 +828,6 @@ class generic_type : public object {
828828
const auto is_static = !(rec_fget->is_method && rec_fget->scope);
829829
const auto has_doc = rec_fget->doc && pybind11::options::show_user_defined_docstrings();
830830

831-
if (is_static) {
832-
auto mclass = handle((PyObject *) Py_TYPE(m_ptr));
833-
834-
if ((PyTypeObject *) mclass.ptr() == &PyType_Type)
835-
pybind11_fail(
836-
"Adding static properties to the type '" +
837-
std::string(((PyTypeObject *) m_ptr)->tp_name) +
838-
"' requires the type to have a custom metaclass. Please "
839-
"ensure that one is created by supplying the pybind11::metaclass() "
840-
"annotation to the associated class_<>(..) invocation.");
841-
}
842-
843831
auto property = handle((PyObject *) (is_static ? get_internals().static_property_type
844832
: &PyProperty_Type));
845833
attr(name) = property(fget.ptr() ? fget : none(),

tests/test_methods_and_attributes.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ test_initializer methods_and_attributes([](py::module &m) {
202202
.def("__str__", &ExampleMandA::toString)
203203
.def_readwrite("value", &ExampleMandA::value);
204204

205-
py::class_<TestProperties>(m, "TestProperties", py::metaclass())
205+
py::class_<TestProperties>(m, "TestProperties")
206206
.def(py::init<>())
207207
.def_readonly("def_readonly", &TestProperties::value)
208208
.def_readwrite("def_readwrite", &TestProperties::value)
@@ -228,7 +228,7 @@ test_initializer methods_and_attributes([](py::module &m) {
228228
auto static_set2 = [](py::object, int v) { TestPropRVP::sv2.value = v; };
229229
auto rvp_copy = py::return_value_policy::copy;
230230

231-
py::class_<TestPropRVP>(m, "TestPropRVP", py::metaclass())
231+
py::class_<TestPropRVP>(m, "TestPropRVP")
232232
.def(py::init<>())
233233
.def_property_readonly("ro_ref", &TestPropRVP::get1)
234234
.def_property_readonly("ro_copy", &TestPropRVP::get2, rvp_copy)

tests/test_multiple_inheritance.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,24 +123,24 @@ test_initializer mi_static_properties([](py::module &pm) {
123123
.def(py::init<>())
124124
.def("vanilla", &Vanilla::vanilla);
125125

126-
py::class_<WithStatic1>(m, "WithStatic1", py::metaclass())
126+
py::class_<WithStatic1>(m, "WithStatic1")
127127
.def(py::init<>())
128128
.def_static("static_func1", &WithStatic1::static_func1)
129129
.def_readwrite_static("static_value1", &WithStatic1::static_value1);
130130

131-
py::class_<WithStatic2>(m, "WithStatic2", py::metaclass())
131+
py::class_<WithStatic2>(m, "WithStatic2")
132132
.def(py::init<>())
133133
.def_static("static_func2", &WithStatic2::static_func2)
134134
.def_readwrite_static("static_value2", &WithStatic2::static_value2);
135135

136136
py::class_<VanillaStaticMix1, Vanilla, WithStatic1, WithStatic2>(
137-
m, "VanillaStaticMix1", py::metaclass())
137+
m, "VanillaStaticMix1")
138138
.def(py::init<>())
139139
.def_static("static_func", &VanillaStaticMix1::static_func)
140140
.def_readwrite_static("static_value", &VanillaStaticMix1::static_value);
141141

142142
py::class_<VanillaStaticMix2, WithStatic1, Vanilla, WithStatic2>(
143-
m, "VanillaStaticMix2", py::metaclass())
143+
m, "VanillaStaticMix2")
144144
.def(py::init<>())
145145
.def_static("static_func", &VanillaStaticMix2::static_func)
146146
.def_readwrite_static("static_value", &VanillaStaticMix2::static_value);

tests/test_python_types.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ struct MoveOutContainer {
190190
test_initializer python_types([](py::module &m) {
191191
/* No constructor is explicitly defined below. An exception is raised when
192192
trying to construct it directly from Python */
193-
py::class_<ExamplePythonTypes>(m, "ExamplePythonTypes", "Example 2 documentation", py::metaclass())
193+
py::class_<ExamplePythonTypes>(m, "ExamplePythonTypes", "Example 2 documentation")
194194
.def("get_dict", &ExamplePythonTypes::get_dict, "Return a Python dictionary")
195195
.def("get_dict_2", &ExamplePythonTypes::get_dict_2, "Return a C++ dictionary")
196196
.def("get_list", &ExamplePythonTypes::get_list, "Return a Python list")

0 commit comments

Comments
 (0)