Skip to content

Commit 1d1f81b

Browse files
authored
WIP: PyPy support (#527)
This commit includes modifications that are needed to get pybind11 to work with PyPy. The full test suite compiles and runs except for a last few functions that are commented out (due to problems in PyPy that were reported on the PyPy bugtracker). Two somewhat intrusive changes were needed to make it possible: two new tags ``py::buffer_protocol()`` and ``py::metaclass()`` must now be specified to the ``class_`` constructor if the class uses the buffer protocol and/or requires a metaclass (e.g. for static properties). Note that this is only for the PyPy version based on Python 2.7 for now. When the PyPy 3.x has caught up in terms of cpyext compliance, a PyPy 3.x patch will follow.
1 parent c79e435 commit 1d1f81b

29 files changed

+408
-178
lines changed

.travis.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ matrix:
2929
- os: osx
3030
osx_image: xcode7.3
3131
env: PYTHON=3.5 CPP=14 CLANG
32+
# Test a PyPy 2.7 nightly build
33+
- os: linux
34+
env: PYPY=1 PYTHON=2.7 CPP=11 GCC=4.8
35+
addons:
36+
apt:
37+
sources: [ubuntu-toolchain-r-test, kubuntu-backports]
38+
packages: [g++-4.8, cmake]
3239
# A barebones build makes sure everything still works without optional deps (numpy/scipy/eigen)
3340
# and also tests the automatic discovery functions in CMake (Python version, C++ standard).
3441
- os: linux
@@ -70,9 +77,18 @@ before_install:
7077
fi
7178
if [ -n "$CPP" ]; then export CPP=-std=c++$CPP; fi
7279
if [ "${PYTHON:0:1}" = "3" ]; then export PY=3; fi
80+
if [ -n "$PYPY" ]; then
81+
curl http://buildbot.pypy.org/nightly/trunk/pypy-c-jit-latest-linux64.tar.bz2 | tar -xj
82+
export PYPY_BINARY=$(echo `pwd`/pypy-c-jit*/bin/pypy)
83+
export CMAKE_EXTRA_ARGS="-DPYTHON_EXECUTABLE:FILEPATH=$PYPY_BINARY"
84+
fi
7385
if [ -n "$DEBUG" ]; then export CMAKE_EXTRA_ARGS="-DCMAKE_BUILD_TYPE=Debug"; fi
7486
- |
75-
# Initialize enviornment
87+
# Initialize environment
88+
if [ -n "$PYPY" ]; then
89+
$PYPY_BINARY -m ensurepip
90+
$PYPY_BINARY -m pip install pytest
91+
fi
7692
if [ -n "$DOCKER" ]; then
7793
docker pull $DOCKER
7894
export containerid=$(docker run --detach --tty \

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ become an excessively large and unnecessary dependency.
2525
Think of this library as a tiny self-contained version of Boost.Python with
2626
everything stripped away that isn't relevant for binding generation. Without
2727
comments, the core header files only require ~2.5K lines of code and depend on
28-
Python (2.7 or 3.x) and the C++ standard library. This compact implementation
28+
Python (2.7 or 3.x, or PyPy2.7 >= 5.5) and the C++ standard library. This compact implementation
2929
was possible thanks to some of the new C++11 language features (specifically:
3030
tuples, lambda functions and variadic templates). Since its creation, this
3131
library has grown beyond Boost.Python in many ways, leading to dramatically
@@ -58,12 +58,15 @@ pybind11 can map the following core C++ features to Python
5858
## Goodies
5959
In addition to the core functionality, pybind11 provides some extra goodies:
6060

61-
- pybind11 uses C++11 move constructors and move assignment operators whenever
62-
possible to efficiently transfer custom data types.
61+
- Python 2.7, 3.x, and PyPy (PyPy2.7 >= 5.5) are supported with an
62+
implementation-agnostic interface.
6363

6464
- It is possible to bind C++11 lambda functions with captured variables. The
6565
lambda capture data is stored inside the resulting Python function object.
6666

67+
- pybind11 uses C++11 move constructors and move assignment operators whenever
68+
possible to efficiently transfer custom data types.
69+
6770
- It's easy to expose the internal storage of custom data types through
6871
Pythons' buffer protocols. This is handy e.g. for fast conversion between
6972
C++ matrix classes like Eigen and NumPy without expensive copy operations.
@@ -100,7 +103,7 @@ In addition to the core functionality, pybind11 provides some extra goodies:
100103

101104
## About
102105

103-
This project was created by [Wenzel Jakob](https://www.mitsuba-renderer.org/~wenzel/).
106+
This project was created by [Wenzel Jakob](http://rgl.epfl.ch/people/wjakob).
104107
Significant features and/or improvements to the code were contributed by
105108
Jonas Adler,
106109
Sylvain Corlay,

docs/advanced/classes.rst

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

424424
Static properties can also be created in a similar way to expose getters and
425-
setters of static class attributes. It is important to note that the implicit
426-
``self`` argument also exists in this case and is used to pass the Python
427-
``type`` subclass instance. This parameter will often not be needed by the C++
428-
side, and the following example illustrates how to instantiate a lambda getter
429-
function that ignores it:
425+
setters of static class attributes. Two things are important to note:
426+
427+
1. Static properties are implemented by instrumenting the *metaclass* of the
428+
class in question -- however, this requires the class to have a modifiable
429+
metaclass in the first place. pybind11 provides a ``py::metaclass()``
430+
annotation that must be specified in the ``class_`` constructor, or any
431+
later method calls to ``def_{property_,∅}_{readwrite,readonly}_static`` will
432+
fail (see the example below).
433+
434+
2. For static properties defined in terms of setter and getter functions, note
435+
that the implicit ``self`` argument also exists in this case and is used to
436+
pass the Python ``type`` subclass instance. This parameter will often not be
437+
needed by the C++ side, and the following example illustrates how to
438+
instantiate a lambda getter function that ignores it:
430439

431440
.. code-block:: cpp
432441
433-
py::class_<Foo>(m, "Foo")
442+
py::class_<Foo>(m, "Foo", py::metaclass())
434443
.def_property_readonly_static("foo", [](py::object /* self */) { return Foo(); });
435444
436445
Operator overloading

docs/advanced/pycpp/numpy.rst

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ completely avoid copy operations with Python expressions like
3333

3434
.. code-block:: cpp
3535
36-
py::class_<Matrix>(m, "Matrix")
36+
py::class_<Matrix>(m, "Matrix", py::buffer_protocol())
3737
.def_buffer([](Matrix &m) -> py::buffer_info {
3838
return py::buffer_info(
3939
m.data(), /* Pointer to buffer */
@@ -46,9 +46,12 @@ completely avoid copy operations with Python expressions like
4646
);
4747
});
4848
49-
The snippet above binds a lambda function, which can create ``py::buffer_info``
50-
description records on demand describing a given matrix. The contents of
51-
``py::buffer_info`` mirror the Python buffer protocol specification.
49+
Supporting the buffer protocol in a new type involves specifying the special
50+
``py::buffer_protocol()`` tag in the ``py::class_`` constructor and calling the
51+
``def_buffer()`` method with a lambda function that creates a
52+
``py::buffer_info`` description record on demand describing a given matrix
53+
instance. The contents of ``py::buffer_info`` mirror the Python buffer protocol
54+
specification.
5255

5356
.. code-block:: cpp
5457
@@ -77,7 +80,7 @@ buffer objects (e.g. a NumPy matrix).
7780
typedef Matrix::Scalar Scalar;
7881
constexpr bool rowMajor = Matrix::Flags & Eigen::RowMajorBit;
7982
80-
py::class_<Matrix>(m, "Matrix")
83+
py::class_<Matrix>(m, "Matrix", py::buffer_protocol())
8184
.def("__init__", [](Matrix &m, py::buffer b) {
8285
typedef Eigen::Stride<Eigen::Dynamic, Eigen::Dynamic> Strides;
8386

docs/intro.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ Goodies
5151
*******
5252
In addition to the core functionality, pybind11 provides some extra goodies:
5353

54+
- Python 2.7, 3.x, and PyPy (PyPy2.7 >= 5.5) are supported with an
55+
implementation-agnostic interface.
56+
5457
- It is possible to bind C++11 lambda functions with captured variables. The
5558
lambda capture data is stored inside the resulting Python function object.
5659

include/pybind11/attr.h

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ struct multiple_inheritance { };
4747
/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class
4848
struct dynamic_attr { };
4949

50+
/// Annotation which enables the buffer protocol for a type
51+
struct buffer_protocol { };
52+
53+
/// Annotation which requests that a special metaclass is created for a type
54+
struct metaclass { };
55+
5056
/// Annotation to mark enums as an arithmetic type
5157
struct arithmetic { };
5258

@@ -136,7 +142,9 @@ struct function_record {
136142

137143
/// Special data structure which (temporarily) holds metadata about a bound class
138144
struct type_record {
139-
PYBIND11_NOINLINE type_record() { }
145+
PYBIND11_NOINLINE type_record()
146+
: multiple_inheritance(false), dynamic_attr(false),
147+
buffer_protocol(false), metaclass(false) { }
140148

141149
/// Handle to the parent scope
142150
handle scope;
@@ -166,10 +174,16 @@ struct type_record {
166174
const char *doc = nullptr;
167175

168176
/// Multiple inheritance marker
169-
bool multiple_inheritance = false;
177+
bool multiple_inheritance : 1;
170178

171179
/// Does the class manage a __dict__?
172-
bool dynamic_attr = false;
180+
bool dynamic_attr : 1;
181+
182+
/// Does the class implement the buffer protocol?
183+
bool buffer_protocol : 1;
184+
185+
/// Does the class require its own metaclass?
186+
bool metaclass : 1;
173187

174188
PYBIND11_NOINLINE void add_base(const std::type_info *base, void *(*caster)(void *)) {
175189
auto base_info = detail::get_type_info(*base, false);
@@ -309,6 +323,16 @@ struct process_attribute<dynamic_attr> : process_attribute_default<dynamic_attr>
309323
static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; }
310324
};
311325

326+
template <>
327+
struct process_attribute<buffer_protocol> : process_attribute_default<buffer_protocol> {
328+
static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; }
329+
};
330+
331+
template <>
332+
struct process_attribute<metaclass> : process_attribute_default<metaclass> {
333+
static void init(const metaclass &, type_record *r) { r->metaclass = true; }
334+
};
335+
312336

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

include/pybind11/cast.h

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,10 @@ PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp, bool t
108108
}
109109

110110
PYBIND11_NOINLINE inline bool isinstance_generic(handle obj, const std::type_info &tp) {
111-
const auto type = detail::get_type_handle(tp, false);
111+
handle type = detail::get_type_handle(tp, false);
112112
if (!type)
113113
return false;
114-
115-
const auto result = PyObject_IsInstance(obj.ptr(), type.ptr());
116-
if (result == -1)
117-
throw error_already_set();
118-
return result != 0;
114+
return isinstance(obj, type);
119115
}
120116

121117
PYBIND11_NOINLINE inline std::string error_string() {
@@ -141,6 +137,7 @@ PYBIND11_NOINLINE inline std::string error_string() {
141137
PyException_SetTraceback(scope.value, scope.trace);
142138
#endif
143139

140+
#if !defined(PYPY_VERSION)
144141
if (scope.trace) {
145142
PyTracebackObject *trace = (PyTracebackObject *) scope.trace;
146143

@@ -160,6 +157,7 @@ PYBIND11_NOINLINE inline std::string error_string() {
160157
}
161158
trace = trace->tb_next;
162159
}
160+
#endif
163161

164162
return errorString;
165163
}
@@ -176,7 +174,9 @@ PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr, const detail:
176174
}
177175

178176
inline PyThreadState *get_thread_state_unchecked() {
179-
#if PY_VERSION_HEX < 0x03000000
177+
#if defined(PYPY_VERSION)
178+
return PyThreadState_GET();
179+
#elif PY_VERSION_HEX < 0x03000000
180180
return _PyThreadState_Current;
181181
#elif PY_VERSION_HEX < 0x03050000
182182
return (PyThreadState*) _Py_atomic_load_relaxed(&_PyThreadState_Current);
@@ -224,7 +224,7 @@ class type_caster_generic {
224224

225225
/* If this is a python class, also check the parents recursively */
226226
auto const &type_dict = get_internals().registered_types_py;
227-
bool new_style_class = PyType_Check(tobj);
227+
bool new_style_class = PyType_Check((PyObject *) tobj);
228228
if (type_dict.find(tobj) == type_dict.end() && new_style_class && tobj->tp_bases) {
229229
auto parents = reinterpret_borrow<tuple>(tobj->tp_bases);
230230
for (handle parent : parents) {
@@ -662,10 +662,10 @@ template <> class type_caster<std::wstring> {
662662
#if PY_MAJOR_VERSION >= 3
663663
buffer = PyUnicode_AsWideCharString(load_src.ptr(), &length);
664664
#else
665-
temp = reinterpret_steal<object>(
666-
sizeof(wchar_t) == sizeof(short)
667-
? PyUnicode_AsUTF16String(load_src.ptr())
668-
: PyUnicode_AsUTF32String(load_src.ptr()));
665+
temp = reinterpret_steal<object>(PyUnicode_AsEncodedString(
666+
load_src.ptr(), sizeof(wchar_t) == sizeof(short)
667+
? "utf16" : "utf32", nullptr));
668+
669669
if (temp) {
670670
int err = PYBIND11_BYTES_AS_STRING_AND_SIZE(temp.ptr(), (char **) &buffer, &length);
671671
if (err == -1) { buffer = nullptr; } // TypeError
@@ -868,7 +868,7 @@ template <typename type, typename holder_type> class type_caster_holder : public
868868

869869
/* If this is a python class, also check the parents recursively */
870870
auto const &type_dict = get_internals().registered_types_py;
871-
bool new_style_class = PyType_Check(tobj);
871+
bool new_style_class = PyType_Check((PyObject *) tobj);
872872
if (type_dict.find(tobj) == type_dict.end() && new_style_class && tobj->tp_bases) {
873873
auto parents = reinterpret_borrow<tuple>(tobj->tp_bases);
874874
for (handle parent : parents) {

include/pybind11/eval.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,15 @@ object eval_file(str fname, object global = object(), object local = object()) {
9595
pybind11_fail("File \"" + fname_str + "\" could not be opened!");
9696
}
9797

98+
#if PY_VERSION_HEX < 0x03000000 && defined(PYPY_VERSION)
99+
PyObject *result = PyRun_File(f, fname_str.c_str(), start, global.ptr(),
100+
local.ptr());
101+
(void) closeFile;
102+
#else
98103
PyObject *result = PyRun_FileEx(f, fname_str.c_str(), start, global.ptr(),
99104
local.ptr(), closeFile);
105+
#endif
106+
100107
if (!result)
101108
throw error_already_set();
102109
return reinterpret_steal<object>(result);

include/pybind11/functional.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ struct type_caster<std::function<Return(Args...) PYBIND11_NOEXCEPT_SPECIFIER>> {
3939
captured variables), in which case the roundtrip can be avoided.
4040
*/
4141
if (PyCFunction_Check(src_.ptr())) {
42-
auto c = reinterpret_borrow<capsule>(PyCFunction_GetSelf(src_.ptr()));
42+
auto c = reinterpret_borrow<capsule>(PyCFunction_GET_SELF(src_.ptr()));
4343
auto rec = (function_record *) c;
4444

4545
if (rec && rec->is_stateless && rec->data[1] == &typeid(function_type)) {

0 commit comments

Comments
 (0)