diff --git a/docs/advanced.rst b/docs/advanced.rst index 75d4e24f6..43a3b1a97 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -801,9 +801,9 @@ For instance, the following statement iterates over a Python ``dict``: } Available types include :class:`handle`, :class:`object`, :class:`bool_`, -:class:`int_`, :class:`float_`, :class:`str`, :class:`tuple`, :class:`list`, -:class:`dict`, :class:`slice`, :class:`capsule`, :class:`function`, -:class:`buffer`, :class:`array`, and :class:`array_t`. +:class:`int_`, :class:`float_`, :class:`str`, :class:`bytes`, :class:`tuple`, +:class:`list`, :class:`dict`, :class:`slice`, :class:`capsule`, +:class:`function`, :class:`buffer`, :class:`array`, and :class:`array_t`. In this kind of mixed code, it is often necessary to convert arbitrary C++ types to Python, which can be done using :func:`cast`: diff --git a/example/example2.cpp b/example/example2.cpp index 647427b83..c4e0d7cd0 100644 --- a/example/example2.cpp +++ b/example/example2.cpp @@ -72,24 +72,12 @@ public: std::cout << "key: " << item.first << ", value=" << item.second << std::endl; } - /* STL data types are automatically casted from Python */ - void print_dict_2(const std::map &dict) { - for (auto item : dict) - std::cout << "key: " << item.first << ", value=" << item.second << std::endl; - } - - /* Easily iterate over a setionary using a C++11 range-based for loop */ + /* Easily iterate over a set using a C++11 range-based for loop */ void print_set(py::set set) { for (auto item : set) std::cout << "key: " << item << std::endl; } - /* STL data types are automatically casted from Python */ - void print_set_2(const std::set &set) { - for (auto item : set) - std::cout << "key: " << item << std::endl; - } - /* Easily iterate over a list using a C++11 range-based for loop */ void print_list(py::list list) { int index = 0; @@ -97,7 +85,19 @@ public: std::cout << "list item " << index++ << ": " << item << std::endl; } - /* STL data types are automatically casted from Python */ + /* STL data types (such as maps) are automatically casted from Python */ + void print_dict_2(const std::map &dict) { + for (auto item : dict) + std::cout << "key: " << item.first << ", value=" << item.second << std::endl; + } + + /* STL data types (such as sets) are automatically casted from Python */ + void print_set_2(const std::set &set) { + for (auto item : set) + std::cout << "key: " << item << std::endl; + } + + /* STL data types (such as vectors) are automatically casted from Python */ void print_list_2(std::vector &list) { int index = 0; for (auto item : list) diff --git a/example/example4.cpp b/example/example4.cpp index ae4dfb3d8..9865931aa 100644 --- a/example/example4.cpp +++ b/example/example4.cpp @@ -1,5 +1,5 @@ /* - example/example4.cpp -- global constants and functions, enumerations + example/example4.cpp -- global constants and functions, enumerations, raw byte strings Copyright (c) 2015 Wenzel Jakob @@ -40,6 +40,17 @@ float test_function3(int i) { return i / 2.f; } +py::bytes return_bytes() { + const char *data = "\x01\x00\x02\x00"; + return py::bytes(std::string(data, 4)); +} + +void print_bytes(py::bytes bytes) { + std::string value = (std::string) bytes; + for (size_t i = 0; i < value.length(); ++i) + std::cout << "bytes[" << i << "]=" << (int) value[i] << std::endl; +} + void init_ex4(py::module &m) { m.def("test_function", &test_function1); m.def("test_function", &test_function2); @@ -57,4 +68,7 @@ void init_ex4(py::module &m) { .value("EFirstMode", Example4::EFirstMode) .value("ESecondMode", Example4::ESecondMode) .export_values(); + + m.def("return_bytes", &return_bytes); + m.def("print_bytes", &print_bytes); } diff --git a/example/example4.py b/example/example4.py index a926c1429..74f860d0c 100755 --- a/example/example4.py +++ b/example/example4.py @@ -8,6 +8,8 @@ from example import some_constant from example import EMyEnumeration from example import EFirstEntry from example import Example4 +from example import return_bytes +from example import print_bytes print(EMyEnumeration) print(EMyEnumeration.EFirstEntry) @@ -23,3 +25,5 @@ print(Example4.EMode) print(Example4.EMode.EFirstMode) print(Example4.EFirstMode) Example4.test_function(Example4.EFirstMode) + +print_bytes(return_bytes()) diff --git a/example/example4.ref b/example/example4.ref index 1d2dd9b38..9be2bdc76 100644 --- a/example/example4.ref +++ b/example/example4.ref @@ -14,3 +14,7 @@ None EMode.EFirstMode EMode.EFirstMode Example4::test_function(enum=1) +bytes[0]=1 +bytes[1]=0 +bytes[2]=2 +bytes[3]=0 diff --git a/example/run_test.py b/example/run_test.py index 7c1bf7d50..8977e9672 100755 --- a/example/run_test.py +++ b/example/run_test.py @@ -21,6 +21,7 @@ def sanitize(lines): line = shorten_floats.sub(r'\1', line) line = line.replace('__builtin__', 'builtins') line = line.replace('example.', '') + line = line.replace('unicode', 'str') line = line.replace('method of builtins.PyCapsule instance', '') line = line.strip() if sys.platform == 'win32': diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index da0831179..a02f68fb7 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -19,12 +19,6 @@ NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(detail) -#if PY_MAJOR_VERSION >= 3 -#define PYBIND11_AS_STRING PyBytes_AsString -#else -#define PYBIND11_AS_STRING PyString_AsString -#endif - class type_caster_custom { public: PYBIND11_NOINLINE type_caster_custom(const std::type_info *type_info) { @@ -185,9 +179,9 @@ public: py_value = (py_type) PyLong_AsUnsignedLong(src); } else { if (std::is_signed::value) - py_value = (py_type) detail::PyLong_AsLongLong_(src); + py_value = (py_type) PYBIND11_LONG_AS_LONGLONG(src); else - py_value = (py_type) detail::PyLong_AsUnsignedLongLong_(src); + py_value = (py_type) PYBIND11_LONG_AS_UNSIGNED_LONGLONG(src); } if ((py_value == (py_type) -1 && PyErr_Occurred()) || @@ -265,35 +259,41 @@ public: template <> class type_caster { public: bool load(PyObject *src, bool) { -#if PY_MAJOR_VERSION < 3 - if (PyString_Check(src)) { value = PyString_AsString(src); return true; } -#endif - object temp(PyUnicode_AsUTF8String(src), false); - const char *ptr = nullptr; - if (temp) - ptr = PYBIND11_AS_STRING(temp.ptr()); - if (!ptr) { PyErr_Clear(); return false; } - value = ptr; + object temp; + PyObject *load_src = src; + if (PyUnicode_Check(src)) { + temp = object(PyUnicode_AsUTF8String(src), false); + if (!temp) { PyErr_Clear(); return false; } // UnicodeEncodeError + load_src = temp.ptr(); + } + char *buffer; + ssize_t length; + int err = PYBIND11_BYTES_AS_STRING_AND_SIZE(load_src, &buffer, &length); + if (err == -1) { PyErr_Clear(); return false; } // TypeError + value = std::string(buffer, length); return true; } + static PyObject *cast(const std::string &src, return_value_policy /* policy */, PyObject * /* parent */) { - return PyUnicode_FromString(src.c_str()); + return PyUnicode_FromStringAndSize(src.c_str(), src.length()); } - PYBIND11_TYPE_CASTER(std::string, _("str")); + + PYBIND11_TYPE_CASTER(std::string, _(PYBIND11_STRING_NAME)); }; template <> class type_caster { public: bool load(PyObject *src, bool) { -#if PY_MAJOR_VERSION < 3 - if (PyString_Check(src)) { value = PyString_AsString(src); return true; } -#endif - object temp(PyUnicode_AsUTF8String(src), false); - const char *ptr = nullptr; - if (temp) - ptr = PYBIND11_AS_STRING(temp.ptr()); - if (!ptr) { PyErr_Clear(); return false; } - value = ptr; + object temp; + PyObject *load_src = src; + if (PyUnicode_Check(src)) { + temp = object(PyUnicode_AsUTF8String(src), false); + if (!temp) { PyErr_Clear(); return false; } // UnicodeEncodeError + load_src = temp.ptr(); + } + const char *ptr = PYBIND11_BYTES_AS_STRING(load_src); + if (!ptr) { PyErr_Clear(); return false; } // TypeError + value = std::string(ptr); return true; } @@ -306,10 +306,10 @@ public: return PyUnicode_DecodeLatin1(str, 1, nullptr); } - static PYBIND11_DESCR name() { return type_descr(_("str")); } - operator char*() { return (char *) value.c_str(); } operator char() { if (value.length() > 0) return value[0]; else return '\0'; } + + static PYBIND11_DESCR name() { return type_descr(_(PYBIND11_STRING_NAME)); } protected: std::string value; }; diff --git a/include/pybind11/common.h b/include/pybind11/common.h index d5d10e822..43b365778 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -85,6 +85,29 @@ } \ PyObject *pybind11_init() +#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions +#define PYBIND11_BYTES_CHECK PyBytes_Check +#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString +#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize +#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize +#define PYBIND11_BYTES_AS_STRING PyBytes_AsString +#define PYBIND11_LONG_CHECK(o) PyLong_Check(o) +#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o) +#define PYBIND11_LONG_AS_UNSIGNED_LONGLONG(o) PyLong_AsUnsignedLongLong(o) +#define PYBIND11_STRING_NAME "str" +#define PYBIND11_SLICE_OBJECT PyObject +#else +#define PYBIND11_BYTES_CHECK PyString_Check +#define PYBIND11_BYTES_FROM_STRING PyString_FromString +#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize +#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize +#define PYBIND11_BYTES_AS_STRING PyString_AsString +#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o)) +#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o)) +#define PYBIND11_LONG_AS_UNSIGNED_LONGLONG(o) (PyInt_Check(o) ? (unsigned long long) PyLong_AsUnsignedLong(o) : PyLong_AsUnsignedLongLong(o)) +#define PYBIND11_STRING_NAME "unicode" +#define PYBIND11_SLICE_OBJECT PySliceObject +#endif NAMESPACE_BEGIN(pybind11) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 10bc6e32b..9827c0a3c 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -337,7 +337,7 @@ private: if (a.descr) a.descr = strdup(a.descr); else if (a.value) - a.descr = strdup(((object) handle(a.value).attr("__repr__")).call().str()); + a.descr = strdup(((std::string) ((object) handle(a.value).attr("__repr__")).call().str()).c_str()); } auto const ®istered_types = detail::get_internals().registered_types; @@ -367,7 +367,7 @@ private: } else if (c == '%') { const std::type_info *t = types[type_index++]; if (!t) - throw std::runtime_error("Internal error while generating type signature (1)"); + throw std::runtime_error("Internal error while parsing type signature (1)"); auto it = registered_types.find(t); if (it != registered_types.end()) { signature += it->second.type->tp_name; @@ -381,7 +381,7 @@ private: } } if (type_depth != 0 && types[type_index ] != nullptr) - throw std::runtime_error("Internal error while generating type signature (2)"); + throw std::runtime_error("Internal error while parsing type signature (2)"); #if !defined(PYBIND11_CPP14) delete[] types; @@ -694,8 +694,8 @@ protected: view->format = const_cast(info->format.c_str()); if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { view->ndim = info->ndim; - view->strides = (Py_ssize_t *)&info->strides[0]; - view->shape = (Py_ssize_t *) &info->shape[0]; + view->strides = (ssize_t *) &info->strides[0]; + view->shape = (ssize_t *) &info->shape[0]; } Py_INCREF(view->obj); return 0; @@ -903,7 +903,7 @@ public: void export_values() { PyObject *dict = ((PyTypeObject *) this->m_ptr)->tp_dict; PyObject *key, *value; - Py_ssize_t pos = 0; + ssize_t pos = 0; while (PyDict_Next(dict, &pos, &key, &value)) if (PyObject_IsInstance(value, this->m_ptr)) m_parent.attr(key) = value; @@ -983,9 +983,10 @@ inline function get_overload(const void *this_ptr, const char *name) { cache.insert(key); return function(); } + PyFrameObject *frame = PyThreadState_Get()->frame; pybind11::str caller = pybind11::handle(frame->f_code->co_name).str(); - if (strcmp((const char *) caller, name) == 0) + if ((std::string) caller == name) return function(); return overload; } diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 8d84afac6..98dc90c0a 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -215,30 +215,6 @@ private: ssize_t pos = 0; }; -#if PY_MAJOR_VERSION >= 3 -inline long long PyLong_AsLongLong_(PyObject *o) { return PyLong_AsLongLong(o); } -inline unsigned long long PyLong_AsUnsignedLongLong_(PyObject *o) { return PyLong_AsUnsignedLongLong(o); } -inline bool PyLong_Check_(PyObject *o) { return PyLong_Check(o); } -#else -inline long long PyLong_AsLongLong_(PyObject *o) { - if (PyInt_Check(o)) /// workaround: PyLong_AsLongLong doesn't accept 'int' on Python 2.x - return (long long) PyLong_AsLong(o); - else - return PyLong_AsLongLong(o); -} - -inline unsigned long long PyLong_AsUnsignedLongLong_(PyObject *o) { - if (PyInt_Check(o)) /// workaround: PyLong_AsUnsignedLongLong doesn't accept 'int' on Python 2.x - return (unsigned long long) PyLong_AsUnsignedLong(o); - else - return PyLong_AsUnsignedLongLong(o); -} - -inline bool PyLong_Check_(PyObject *o) { - return PyInt_Check(o) || PyLong_Check(o); -} -#endif - NAMESPACE_END(detail) inline detail::accessor handle::operator[](handle key) const { return detail::accessor(ptr(), key.ptr(), false); } @@ -266,21 +242,18 @@ inline iterator handle::end() const { return iterator(nullptr); } class str : public object { public: PYBIND11_OBJECT_DEFAULT(str, object, PyUnicode_Check) - str(const char *s) : object(PyUnicode_FromString(s), false) { } - operator const char *() const { + str(const std::string &s) : object(PyUnicode_FromStringAndSize(s.c_str(), s.length()), false) { } + + operator std::string() const { #if PY_MAJOR_VERSION >= 3 return PyUnicode_AsUTF8(m_ptr); #else - m_temp = object(PyUnicode_AsUTF8String(m_ptr), false); - if (m_temp.ptr() == nullptr) - return nullptr; - return PyString_AsString(m_temp.ptr()); + object temp(PyUnicode_AsUTF8String(m_ptr), false); + if (temp.ptr() == nullptr) + throw std::runtime_error("Unable to extract string contents!"); + return PyString_AsString(temp.ptr()); #endif } -private: -#if PY_MAJOR_VERSION < 3 - mutable object m_temp; -#endif }; inline pybind11::str handle::str() const { @@ -292,6 +265,23 @@ inline pybind11::str handle::str() const { return pybind11::str(str, false); } +class bytes : public object { +public: + PYBIND11_OBJECT_DEFAULT(bytes, object, PYBIND11_BYTES_CHECK) + + bytes(const std::string &s) + : object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(s.data(), s.size()), false) { } + + operator std::string() const { + char *buffer; + ssize_t length; + int err = PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length); + if (err == -1) + throw std::runtime_error("Unable to extract bytes contents!"); + return std::string(buffer, length); + } +}; + class bool_ : public object { public: PYBIND11_OBJECT_DEFAULT(bool_, object, PyBool_Check) @@ -301,7 +291,7 @@ public: class int_ : public object { public: - PYBIND11_OBJECT_DEFAULT(int_, object, detail::PyLong_Check_) + PYBIND11_OBJECT_DEFAULT(int_, object, PYBIND11_LONG_CHECK) template ::value, int>::type = 0> int_(T value) { @@ -328,9 +318,9 @@ public: return (T) PyLong_AsUnsignedLong(m_ptr); } else { if (std::is_signed::value) - return (T) detail::PyLong_AsLongLong_(m_ptr); + return (T) PYBIND11_LONG_AS_LONGLONG(m_ptr); else - return (T) detail::PyLong_AsUnsignedLongLong_(m_ptr); + return (T) PYBIND11_LONG_AS_UNSIGNED_LONGLONG(m_ptr); } } }; @@ -352,13 +342,8 @@ public: m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr()); } bool compute(ssize_t length, ssize_t *start, ssize_t *stop, ssize_t *step, ssize_t *slicelength) const { - return PySlice_GetIndicesEx( -#if PY_MAJOR_VERSION >= 3 - m_ptr, -#else - (PySliceObject *) m_ptr, -#endif - length, start, stop, step, slicelength) == 0; + return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, length, + start, stop, step, slicelength) == 0; } }; @@ -377,7 +362,7 @@ public: class tuple : public object { public: PYBIND11_OBJECT(tuple, object, PyTuple_Check) - tuple(size_t size = 0) : object(PyTuple_New((Py_ssize_t) size), false) { } + tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), false) { } size_t size() const { return (size_t) PyTuple_Size(m_ptr); } detail::tuple_accessor operator[](size_t index) const { return detail::tuple_accessor(ptr(), index); } }; @@ -466,11 +451,11 @@ inline std::string error_string() { return ""; if (tstate->curexc_type) { - errorString += (const char *) handle(tstate->curexc_type).str(); + errorString += (std::string) handle(tstate->curexc_type).str(); errorString += ": "; } if (tstate->curexc_value) - errorString += (const char *) handle(tstate->curexc_value).str(); + errorString += (std::string) handle(tstate->curexc_value).str(); return errorString; } diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index 3bff58216..3f3da7f30 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -131,7 +131,7 @@ public: NAMESPACE_END(detail) -inline std::ostream &operator<<(std::ostream &os, const object &obj) { os << (const char *) obj.str(); return os; } +inline std::ostream &operator<<(std::ostream &os, const object &obj) { os << (std::string) obj.str(); return os; } NAMESPACE_END(pybind11)