Merge pull request #372 from dean0x7d/keywords

Keyword arguments and generalized unpacking for C++ API
This commit is contained in:
Wenzel Jakob 2016-09-07 00:31:03 +09:00 committed by GitHub
commit a3dbdc67f5
16 changed files with 576 additions and 131 deletions

View File

@ -1622,24 +1622,76 @@ It is also possible to call python functions via ``operator()``.
py::object result_py = f(1234, "hello", some_instance);
MyClass &result = result_py.cast<MyClass>();
The special ``f(*args)`` and ``f(*args, **kwargs)`` syntax is also supported to
supply arbitrary argument and keyword lists, although these cannot be mixed
with other parameters.
Keyword arguments are also supported. In Python, there is the usual call syntax:
.. code-block:: python
def f(number, say, to):
... # function code
f(1234, say="hello", to=some_instance) # keyword call in Python
In C++, the same call can be made using:
.. code-block:: cpp
py::function f = <...>;
using pybind11::literals; // to bring in the `_a` literal
f(1234, "say"_a="hello", "to"_a=some_instance); // keyword call in C++
Unpacking of ``*args`` and ``**kwargs`` is also possible and can be mixed with
other arguments:
.. code-block:: cpp
// * unpacking
py::tuple args = py::make_tuple(1234, "hello", some_instance);
f(*args);
// ** unpacking
py::dict kwargs = py::dict("number"_a=1234, "say"_a="hello", "to"_a=some_instance);
f(**kwargs);
// mixed keywords, * and ** unpacking
py::tuple args = py::make_tuple(1234);
py::dict kwargs;
kwargs["y"] = py::cast(5678);
py::object result = f(*args, **kwargs);
py::dict kwargs = py::dict("to"_a=some_instance);
f(*args, "say"_a="hello", **kwargs);
Generalized unpacking according to PEP448_ is also supported:
.. code-block:: cpp
py::dict kwargs1 = py::dict("number"_a=1234);
py::dict kwargs2 = py::dict("to"_a=some_instance);
f(**kwargs1, "say"_a="hello", **kwargs2);
.. seealso::
The file :file:`tests/test_python_types.cpp` contains a complete
example that demonstrates passing native Python types in more detail. The
file :file:`tests/test_kwargs_and_defaults.cpp` discusses usage
of ``args`` and ``kwargs``.
file :file:`tests/test_callbacks.cpp` presents a few examples of calling
Python functions from C++, including keywords arguments and unpacking.
.. _PEP448: https://www.python.org/dev/peps/pep-0448/
Using Python's print function in C++
====================================
The usual way to write output in C++ is using ``std::cout`` while in Python one
would use ``print``. Since these methods use different buffers, mixing them can
lead to output order issues. To resolve this, pybind11 modules can use the
:func:`py::print` function which writes to Python's ``sys.stdout`` for consistency.
Python's ``print`` function is replicated in the C++ API including optional
keyword arguments ``sep``, ``end``, ``file``, ``flush``. Everything works as
expected in Python:
.. code-block:: cpp
py::print(1, 2.0, "three"); // 1 2.0 three
py::print(1, 2.0, "three", "sep"_a="-"); // 1-2.0-three
auto args = py::make_tuple("unpacked", true);
py::print("->", *args, "end"_a="<-"); // -> unpacked True <-
Default arguments revisited
===========================

View File

@ -46,6 +46,13 @@ Breaking changes queued for v2.0.0 (Not yet released)
* Added constructors for ``str`` and ``bytes`` from zero-terminated char pointers,
and from char pointers and length.
* Added ``memoryview`` wrapper type which is constructible from ``buffer_info``.
* New syntax to call a Python function from C++ using keyword arguments and unpacking,
e.g. ``foo(1, 2, "z"_a=3)`` or ``bar(1, *args, "z"_a=3, **kwargs)``.
* Added ``py::print()`` function which replicates Python's API and writes to Python's
``sys.stdout`` by default (as opposed to C's ``stdout`` like ``std::cout``).
* Added ``py::dict`` keyword constructor:``auto d = dict("number"_a=42, "name"_a="World");``
* Added ``py::str::format()`` method and ``_s`` literal:
``py::str s = "1 + 2 = {}"_s.format(3);``
* Various minor improvements of library internals (no user-visible changes)
1.8.1 (July 12, 2016)

View File

@ -14,35 +14,6 @@
NAMESPACE_BEGIN(pybind11)
template <typename T> struct arg_t;
/// Annotation for keyword arguments
struct arg {
constexpr explicit arg(const char *name) : name(name) { }
template <typename T>
constexpr arg_t<T> operator=(const T &value) const { return {name, value}; }
template <typename T, size_t N>
constexpr arg_t<const T *> operator=(T const (&value)[N]) const {
return operator=((const T *) value);
}
const char *name;
};
/// Annotation for keyword arguments with default values
template <typename T> struct arg_t : public arg {
constexpr arg_t(const char *name, const T &value, const char *descr = nullptr)
: arg(name), value(value), descr(descr) { }
T value;
const char *descr;
};
inline namespace literals {
/// String literal version of arg
constexpr arg operator"" _a(const char *name, size_t) { return arg(name); }
}
/// Annotation for methods
struct is_method { handle class_; is_method(const handle &c) : class_(c) { } };
@ -238,21 +209,14 @@ template <> struct process_attribute<arg> : process_attribute_default<arg> {
};
/// Process a keyword argument attribute (*with* a default value)
template <typename T>
struct process_attribute<arg_t<T>> : process_attribute_default<arg_t<T>> {
static void init(const arg_t<T> &a, function_record *r) {
template <> struct process_attribute<arg_v> : process_attribute_default<arg_v> {
static void init(const arg_v &a, function_record *r) {
if (r->class_ && r->args.empty())
r->args.emplace_back("self", nullptr, handle());
/* Convert keyword value into a Python object */
object o = object(detail::type_caster<typename detail::intrinsic_type<T>::type>::cast(
a.value, return_value_policy::automatic, handle()), false);
if (!o) {
if (!a.value) {
#if !defined(NDEBUG)
std::string descr(typeid(T).name());
detail::clean_type_id(descr);
descr = "'" + std::string(a.name) + ": " + descr + "'";
auto descr = "'" + std::string(a.name) + ": " + a.type + "'";
if (r->class_) {
if (r->name)
descr += " in method '" + (std::string) r->class_.str() + "." + (std::string) r->name + "'";
@ -269,7 +233,7 @@ struct process_attribute<arg_t<T>> : process_attribute_default<arg_t<T>> {
"Compile in debug mode for more information.");
#endif
}
r->args.emplace_back(a.name, a.descr, o.release());
r->args.emplace_back(a.name, a.descr, a.value.inc_ref());
}
};
@ -301,9 +265,6 @@ template <int Nurse, int Patient> struct process_attribute<keep_alive<Nurse, Pat
static void postcall(handle args, handle ret) { keep_alive_impl(Nurse, Patient, args, ret); }
};
/// Ignore that a variable is unused in compiler warnings
inline void ignore_unused(const int *) { }
/// Recursively iterate over variadic template arguments
template <typename... Args> struct process_attributes {
static void init(const Args&... args, function_record *r) {
@ -324,11 +285,6 @@ template <typename... Args> struct process_attributes {
}
};
/// Compile-time integer sum
constexpr size_t constexpr_sum() { return 0; }
template <typename T, typename... Ts>
constexpr size_t constexpr_sum(T n, Ts... ns) { return n + constexpr_sum(ns...); }
/// Check the number of named arguments at compile time
template <typename... Extra,
size_t named = constexpr_sum(std::is_base_of<arg, Extra>::value...),

View File

@ -57,6 +57,7 @@ PYBIND11_NOINLINE inline internals &get_internals() {
} catch (const index_error &e) { PyErr_SetString(PyExc_IndexError, e.what()); return;
} catch (const key_error &e) { PyErr_SetString(PyExc_KeyError, e.what()); return;
} catch (const value_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
} catch (const type_error &e) { PyErr_SetString(PyExc_TypeError, e.what()); return;
} catch (const stop_iteration &e) { PyErr_SetString(PyExc_StopIteration, e.what()); return;
} catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return;
} catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return;
@ -251,8 +252,8 @@ protected:
/* Determine suitable casting operator */
template <typename T>
using cast_op_type = typename std::conditional<std::is_pointer<typename std::remove_reference<T>::type>::value,
typename std::add_pointer<typename intrinsic_type<T>::type>::type,
typename std::add_lvalue_reference<typename intrinsic_type<T>::type>::type>::type;
typename std::add_pointer<intrinsic_t<T>>::type,
typename std::add_lvalue_reference<intrinsic_t<T>>::type>::type;
/// Generic type caster for objects stored on the heap
template <typename type> class type_caster_base : public type_caster_generic {
@ -308,6 +309,7 @@ protected:
};
template <typename type, typename SFINAE = void> class type_caster : public type_caster_base<type> { };
template <typename type> using make_caster = type_caster<intrinsic_t<type>>;
template <typename type> class type_caster<std::reference_wrapper<type>> : public type_caster_base<type> {
public:
@ -610,8 +612,8 @@ public:
}
static handle cast(const type &src, return_value_policy policy, handle parent) {
object o1 = object(type_caster<typename intrinsic_type<T1>::type>::cast(src.first, policy, parent), false);
object o2 = object(type_caster<typename intrinsic_type<T2>::type>::cast(src.second, policy, parent), false);
object o1 = object(make_caster<T1>::cast(src.first, policy, parent), false);
object o2 = object(make_caster<T2>::cast(src.second, policy, parent), false);
if (!o1 || !o2)
return handle();
tuple result(2);
@ -622,24 +624,24 @@ public:
static PYBIND11_DESCR name() {
return type_descr(
_("Tuple[") + type_caster<typename intrinsic_type<T1>::type>::name() +
_(", ") + type_caster<typename intrinsic_type<T2>::type>::name() + _("]"));
_("Tuple[") + make_caster<T1>::name() + _(", ") + make_caster<T2>::name() + _("]")
);
}
template <typename T> using cast_op_type = type;
operator type() {
return type(first .operator typename type_caster<typename intrinsic_type<T1>::type>::template cast_op_type<T1>(),
second.operator typename type_caster<typename intrinsic_type<T2>::type>::template cast_op_type<T2>());
return type(first.operator typename make_caster<T1>::template cast_op_type<T1>(),
second.operator typename make_caster<T2>::template cast_op_type<T2>());
}
protected:
type_caster<typename intrinsic_type<T1>::type> first;
type_caster<typename intrinsic_type<T2>::type> second;
make_caster<T1> first;
make_caster<T2> second;
};
template <typename... Tuple> class type_caster<std::tuple<Tuple...>> {
typedef std::tuple<Tuple...> type;
typedef std::tuple<typename intrinsic_type<Tuple>::type...> itype;
typedef std::tuple<intrinsic_t<Tuple>...> itype;
typedef std::tuple<args> args_type;
typedef std::tuple<args, kwargs> args_kwargs_type;
public:
@ -679,7 +681,7 @@ public:
}
static PYBIND11_DESCR element_names() {
return detail::concat(type_caster<typename intrinsic_type<Tuple>::type>::name()...);
return detail::concat(make_caster<Tuple>::name()...);
}
static PYBIND11_DESCR name() {
@ -704,12 +706,12 @@ public:
protected:
template <typename ReturnValue, typename Func, size_t ... Index> ReturnValue call(Func &&f, index_sequence<Index...>) {
return f(std::get<Index>(value)
.operator typename type_caster<typename intrinsic_type<Tuple>::type>::template cast_op_type<Tuple>()...);
.operator typename make_caster<Tuple>::template cast_op_type<Tuple>()...);
}
template <size_t ... Index> type cast(index_sequence<Index...>) {
return type(std::get<Index>(value)
.operator typename type_caster<typename intrinsic_type<Tuple>::type>::template cast_op_type<Tuple>()...);
.operator typename make_caster<Tuple>::template cast_op_type<Tuple>()...);
}
template <size_t ... Indices> bool load(handle src, bool convert, index_sequence<Indices...>) {
@ -726,7 +728,7 @@ protected:
/* Implementation: Convert a C++ tuple into a Python tuple */
template <size_t ... Indices> static handle cast(const type &src, return_value_policy policy, handle parent, index_sequence<Indices...>) {
std::array<object, size> entries {{
object(type_caster<typename intrinsic_type<Tuple>::type>::cast(std::get<Indices>(src), policy, parent), false)...
object(make_caster<Tuple>::cast(std::get<Indices>(src), policy, parent), false)...
}};
for (const auto &entry: entries)
if (!entry)
@ -739,7 +741,7 @@ protected:
}
protected:
std::tuple<type_caster<typename intrinsic_type<Tuple>::type>...> value;
std::tuple<make_caster<Tuple>...> value;
};
/// Type caster for holder types like std::shared_ptr, etc.
@ -846,7 +848,7 @@ template <typename T> using move_never = std::integral_constant<bool, !move_alwa
NAMESPACE_END(detail)
template <typename T> T cast(const handle &handle) {
typedef detail::type_caster<typename detail::intrinsic_type<T>::type> type_caster;
using type_caster = detail::make_caster<T>;
type_caster conv;
if (!conv.load(handle, true)) {
#if defined(NDEBUG)
@ -866,7 +868,7 @@ template <typename T> object cast(const T &value,
policy = std::is_pointer<T>::value ? return_value_policy::take_ownership : return_value_policy::copy;
else if (policy == return_value_policy::automatic_reference)
policy = std::is_pointer<T>::value ? return_value_policy::reference : return_value_policy::copy;
return object(detail::type_caster<typename detail::intrinsic_type<T>::type>::cast(value, policy, parent), false);
return object(detail::make_caster<T>::cast(value, policy, parent), false);
}
template <typename T> T handle::cast() const { return pybind11::cast<T>(*this); }
@ -927,7 +929,7 @@ template <return_value_policy policy = return_value_policy::automatic_reference,
typename... Args> tuple make_tuple(Args&&... args_) {
const size_t size = sizeof...(Args);
std::array<object, size> args {
{ object(detail::type_caster<typename detail::intrinsic_type<Args>::type>::cast(
{ object(detail::make_caster<Args>::cast(
std::forward<Args>(args_), policy, nullptr), false)... }
};
for (auto &arg_value : args) {
@ -947,13 +949,220 @@ template <return_value_policy policy = return_value_policy::automatic_reference,
return result;
}
template <return_value_policy policy,
typename... Args> object handle::operator()(Args&&... args) const {
tuple args_tuple = pybind11::make_tuple<policy>(std::forward<Args>(args)...);
object result(PyObject_CallObject(m_ptr, args_tuple.ptr()), false);
if (!result)
throw error_already_set();
return result;
/// Annotation for keyword arguments
struct arg {
constexpr explicit arg(const char *name) : name(name) { }
template <typename T> arg_v operator=(T &&value) const;
const char *name;
};
/// Annotation for keyword arguments with values
struct arg_v : arg {
template <typename T>
arg_v(const char *name, T &&x, const char *descr = nullptr)
: arg(name),
value(detail::make_caster<T>::cast(x, return_value_policy::automatic, handle()), false),
descr(descr)
#if !defined(NDEBUG)
, type(type_id<T>())
#endif
{ }
object value;
const char *descr;
#if !defined(NDEBUG)
std::string type;
#endif
};
template <typename T>
arg_v arg::operator=(T &&value) const { return {name, std::forward<T>(value)}; }
/// Alias for backward compatibility -- to be remove in version 2.0
template <typename /*unused*/> using arg_t = arg_v;
inline namespace literals {
/// String literal version of arg
constexpr arg operator"" _a(const char *name, size_t) { return arg(name); }
}
NAMESPACE_BEGIN(detail)
NAMESPACE_BEGIN(constexpr_impl)
/// Implementation details for constexpr functions
constexpr int first(int i) { return i; }
template <typename T, typename... Ts>
constexpr int first(int i, T v, Ts... vs) { return v ? i : first(i + 1, vs...); }
constexpr int last(int /*i*/, int result) { return result; }
template <typename T, typename... Ts>
constexpr int last(int i, int result, T v, Ts... vs) { return last(i + 1, v ? i : result, vs...); }
NAMESPACE_END(constexpr_impl)
/// Return the index of the first type in Ts which satisfies Predicate<T>
template <template<typename> class Predicate, typename... Ts>
constexpr int constexpr_first() { return constexpr_impl::first(0, Predicate<Ts>::value...); }
/// Return the index of the last type in Ts which satisfies Predicate<T>
template <template<typename> class Predicate, typename... Ts>
constexpr int constexpr_last() { return constexpr_impl::last(0, -1, Predicate<Ts>::value...); }
/// Helper class which collects only positional arguments for a Python function call.
/// A fancier version below can collect any argument, but this one is optimal for simple calls.
template <return_value_policy policy>
class simple_collector {
public:
template <typename... Ts>
simple_collector(Ts &&...values)
: m_args(pybind11::make_tuple<policy>(std::forward<Ts>(values)...)) { }
const tuple &args() const & { return m_args; }
dict kwargs() const { return {}; }
tuple args() && { return std::move(m_args); }
/// Call a Python function and pass the collected arguments
object call(PyObject *ptr) const {
auto result = object(PyObject_CallObject(ptr, m_args.ptr()), false);
if (!result)
throw error_already_set();
return result;
}
private:
tuple m_args;
};
/// Helper class which collects positional, keyword, * and ** arguments for a Python function call
template <return_value_policy policy>
class unpacking_collector {
public:
template <typename... Ts>
unpacking_collector(Ts &&...values) {
// Tuples aren't (easily) resizable so a list is needed for collection,
// but the actual function call strictly requires a tuple.
auto args_list = list();
int _[] = { 0, (process(args_list, std::forward<Ts>(values)), 0)... };
ignore_unused(_);
m_args = object(PyList_AsTuple(args_list.ptr()), false);
}
const tuple &args() const & { return m_args; }
const dict &kwargs() const & { return m_kwargs; }
tuple args() && { return std::move(m_args); }
dict kwargs() && { return std::move(m_kwargs); }
/// Call a Python function and pass the collected arguments
object call(PyObject *ptr) const {
auto result = object(PyObject_Call(ptr, m_args.ptr(), m_kwargs.ptr()), false);
if (!result)
throw error_already_set();
return result;
}
private:
template <typename T>
void process(list &args_list, T &&x) {
auto o = object(detail::make_caster<T>::cast(std::forward<T>(x), policy, nullptr), false);
if (!o) {
#if defined(NDEBUG)
argument_cast_error();
#else
argument_cast_error(std::to_string(args_list.size()), type_id<T>());
#endif
}
args_list.append(o);
}
void process(list &args_list, detail::args_proxy ap) {
for (const auto &a : ap) {
args_list.append(a.cast<object>());
}
}
void process(list &/*args_list*/, arg_v a) {
if (m_kwargs[a.name]) {
#if defined(NDEBUG)
multiple_values_error();
#else
multiple_values_error(a.name);
#endif
}
if (!a.value) {
#if defined(NDEBUG)
argument_cast_error();
#else
argument_cast_error(a.name, a.type);
#endif
}
m_kwargs[a.name] = a.value;
}
void process(list &/*args_list*/, detail::kwargs_proxy kp) {
for (const auto &k : dict(kp, true)) {
if (m_kwargs[k.first]) {
#if defined(NDEBUG)
multiple_values_error();
#else
multiple_values_error(k.first.str());
#endif
}
m_kwargs[k.first] = k.second;
}
}
[[noreturn]] static void multiple_values_error() {
throw type_error("Got multiple values for keyword argument "
"(compile in debug mode for details)");
}
[[noreturn]] static void multiple_values_error(std::string name) {
throw type_error("Got multiple values for keyword argument '" + name + "'");
}
[[noreturn]] static void argument_cast_error() {
throw cast_error("Unable to convert call argument to Python object "
"(compile in debug mode for details)");
}
[[noreturn]] static void argument_cast_error(std::string name, std::string type) {
throw cast_error("Unable to convert call argument '" + name
+ "' of type '" + type + "' to Python object");
}
private:
tuple m_args;
dict m_kwargs;
};
/// Collect only positional arguments for a Python function call
template <return_value_policy policy, typename... Args,
typename = enable_if_t<all_of_t<is_positional, Args...>::value>>
simple_collector<policy> collect_arguments(Args &&...args) {
return {std::forward<Args>(args)...};
}
/// Collect all arguments, including keywords and unpacking (only instantiated when needed)
template <return_value_policy policy, typename... Args,
typename = enable_if_t<!all_of_t<is_positional, Args...>::value>>
unpacking_collector<policy> collect_arguments(Args &&...args) {
// Following argument order rules for generalized unpacking according to PEP 448
static_assert(
constexpr_last<is_positional, Args...>() < constexpr_first<is_keyword_or_ds, Args...>()
&& constexpr_last<is_s_unpacking, Args...>() < constexpr_first<is_ds_unpacking, Args...>(),
"Invalid function call: positional args must precede keywords and ** unpacking; "
"* unpacking must precede ** unpacking"
);
return {std::forward<Args>(args)...};
}
NAMESPACE_END(detail)
template <return_value_policy policy, typename... Args>
object handle::operator()(Args &&...args) const {
return detail::collect_arguments<policy>(std::forward<Args>(args)...).call(m_ptr);
}
template <return_value_policy policy,
@ -961,20 +1170,6 @@ template <return_value_policy policy,
return operator()<policy>(std::forward<Args>(args)...);
}
inline object handle::operator()(detail::args_proxy args) const {
object result(PyObject_CallObject(m_ptr, args.ptr()), false);
if (!result)
throw error_already_set();
return result;
}
inline object handle::operator()(detail::args_proxy args, detail::kwargs_proxy kwargs) const {
object result(PyObject_Call(m_ptr, args.ptr(), kwargs.ptr()), false);
if (!result)
throw error_already_set();
return result;
}
#define PYBIND11_MAKE_OPAQUE(Type) \
namespace pybind11 { namespace detail { \
template<> class type_caster<Type> : public type_caster_base<Type> { }; \

View File

@ -326,10 +326,45 @@ template <typename T> struct intrinsic_type<T&> { typedef type
template <typename T> struct intrinsic_type<T&&> { typedef typename intrinsic_type<T>::type type; };
template <typename T, size_t N> struct intrinsic_type<const T[N]> { typedef typename intrinsic_type<T>::type type; };
template <typename T, size_t N> struct intrinsic_type<T[N]> { typedef typename intrinsic_type<T>::type type; };
template <typename T> using intrinsic_t = typename intrinsic_type<T>::type;
/// Helper type to replace 'void' in some expressions
struct void_type { };
/// from __cpp_future__ import (convenient aliases from C++14/17)
template <bool B> using bool_constant = std::integral_constant<bool, B>;
template <class T> using negation = bool_constant<!T::value>;
template <bool B, typename T = void> using enable_if_t = typename std::enable_if<B, T>::type;
template <bool B, typename T, typename F> using conditional_t = typename std::conditional<B, T, F>::type;
/// Compile-time integer sum
constexpr size_t constexpr_sum() { return 0; }
template <typename T, typename... Ts>
constexpr size_t constexpr_sum(T n, Ts... ns) { return size_t{n} + constexpr_sum(ns...); }
/// Return true if all/any Ts satify Predicate<T>
#if !defined(_MSC_VER)
template <template<typename> class Predicate, typename... Ts>
using all_of_t = bool_constant<(constexpr_sum(Predicate<Ts>::value...) == sizeof...(Ts))>;
template <template<typename> class Predicate, typename... Ts>
using any_of_t = bool_constant<(constexpr_sum(Predicate<Ts>::value...) > 0)>;
#else
// MSVC workaround (2015 Update 3 has issues with some member type aliases and constexpr)
template <template<typename> class P, typename...> struct all_of_t : std::true_type { };
template <template<typename> class P, typename T, typename... Ts>
struct all_of_t<P, T, Ts...> : conditional_t<P<T>::value, all_of_t<P, Ts...>, std::false_type> { };
template <template<typename> class P, typename...> struct any_of_t : std::false_type { };
template <template<typename> class P, typename T, typename... Ts>
struct any_of_t<P, T, Ts...> : conditional_t<P<T>::value, std::true_type, any_of_t<P, Ts...>> { };
#endif
/// Defer the evaluation of type T until types Us are instantiated
template <typename T, typename... /*Us*/> struct deferred_type { using type = T; };
template <typename T, typename... Us> using deferred_t = typename deferred_type<T, Us...>::type;
/// Ignore that a variable is unused in compiler warnings
inline void ignore_unused(const int *) { }
NAMESPACE_END(detail)
#define PYBIND11_RUNTIME_EXCEPTION(name) \
@ -345,6 +380,7 @@ PYBIND11_RUNTIME_EXCEPTION(stop_iteration)
PYBIND11_RUNTIME_EXCEPTION(index_error)
PYBIND11_RUNTIME_EXCEPTION(key_error)
PYBIND11_RUNTIME_EXCEPTION(value_error)
PYBIND11_RUNTIME_EXCEPTION(type_error)
PYBIND11_RUNTIME_EXCEPTION(cast_error) /// Thrown when pybind11::cast or handle::call fail due to a type casting error
PYBIND11_RUNTIME_EXCEPTION(reference_cast_error) /// Used internally

View File

@ -1233,6 +1233,33 @@ public:
}
};
NAMESPACE_BEGIN(detail)
PYBIND11_NOINLINE inline void print(tuple args, dict kwargs) {
auto strings = tuple(args.size());
for (size_t i = 0; i < args.size(); ++i) {
strings[i] = args[i].cast<object>().str();
}
auto sep = kwargs["sep"] ? kwargs["sep"] : cast(" ");
auto line = sep.attr("join").cast<object>()(strings);
auto file = kwargs["file"] ? kwargs["file"].cast<object>()
: module::import("sys").attr("stdout");
auto write = file.attr("write").cast<object>();
write(line);
write(kwargs["end"] ? kwargs["end"] : cast("\n"));
if (kwargs["flush"] && kwargs["flush"].cast<bool>()) {
file.attr("flush").cast<object>()();
}
}
NAMESPACE_END(detail)
template <return_value_policy policy = return_value_policy::automatic_reference, typename... Args>
void print(Args &&...args) {
auto c = detail::collect_arguments<policy>(std::forward<Args>(args)...);
detail::print(c.args(), c.kwargs());
}
#if defined(WITH_THREAD)
/* The functions below essentially reproduce the PyGILState_* API using a RAII

View File

@ -16,7 +16,8 @@
NAMESPACE_BEGIN(pybind11)
/* A few forward declarations */
class object; class str; class object; class dict; class iterator;
class object; class str; class iterator;
struct arg; struct arg_v;
namespace detail { class accessor; class args_proxy; class kwargs_proxy; }
/// Holds a reference to a Python object (no reference counting)
@ -47,8 +48,6 @@ public:
object call(Args&&... args) const;
template <return_value_policy policy = return_value_policy::automatic_reference, typename ... Args>
object operator()(Args&&... args) const;
inline object operator()(detail::args_proxy args) const;
inline object operator()(detail::args_proxy f_args, detail::kwargs_proxy kwargs) const;
operator bool() const { return m_ptr != nullptr; }
bool operator==(const handle &h) const { return m_ptr == h.m_ptr; }
bool operator!=(const handle &h) const { return m_ptr != h.m_ptr; }
@ -249,6 +248,23 @@ public:
kwargs_proxy operator*() const { return kwargs_proxy(*this); }
};
/// Python argument categories (using PEP 448 terms)
template <typename T> using is_keyword = std::is_base_of<arg, T>;
template <typename T> using is_s_unpacking = std::is_same<args_proxy, T>; // * unpacking
template <typename T> using is_ds_unpacking = std::is_same<kwargs_proxy, T>; // ** unpacking
template <typename T> using is_positional = bool_constant<
!is_keyword<T>::value && !is_s_unpacking<T>::value && !is_ds_unpacking<T>::value
>;
template <typename T> using is_keyword_or_ds = bool_constant<
is_keyword<T>::value || is_ds_unpacking<T>::value
>;
// Call argument collector forward declarations
template <return_value_policy policy = return_value_policy::automatic_reference>
class simple_collector;
template <return_value_policy policy = return_value_policy::automatic_reference>
class unpacking_collector;
NAMESPACE_END(detail)
#define PYBIND11_OBJECT_CVT(Name, Parent, CheckFun, CvtStmt) \
@ -373,8 +389,18 @@ public:
pybind11_fail("Unable to extract string contents! (invalid type)");
return std::string(buffer, (size_t) length);
}
template <typename... Args>
str format(Args &&...args) const {
return attr("format").cast<object>()(std::forward<Args>(args)...);
}
};
inline namespace literals {
/// String literal version of str
inline str operator"" _s(const char *s, size_t size) { return {s, size}; }
}
inline pybind11::str handle::str() const {
PyObject *strValue = PyObject_Str(m_ptr);
#if PY_MAJOR_VERSION < 3
@ -567,6 +593,12 @@ public:
dict() : object(PyDict_New(), false) {
if (!m_ptr) pybind11_fail("Could not allocate dict object!");
}
template <typename... Args,
typename = detail::enable_if_t<detail::all_of_t<detail::is_keyword_or_ds, Args...>::value>,
// MSVC workaround: it can't compile an out-of-line definition, so defer the collector
typename collector = detail::deferred_t<detail::unpacking_collector<>, Args...>>
dict(Args &&...args) : dict(collector(std::forward<Args>(args)...).kwargs()) { }
size_t size() const { return (size_t) PyDict_Size(m_ptr); }
detail::dict_iterator begin() const { return (++detail::dict_iterator(*this, 0)); }
detail::dict_iterator end() const { return detail::dict_iterator(); }

View File

@ -26,8 +26,8 @@ NAMESPACE_BEGIN(pybind11)
NAMESPACE_BEGIN(detail)
template <typename Type, typename Key> struct set_caster {
typedef Type type;
typedef type_caster<typename intrinsic_type<Key>::type> key_conv;
using type = Type;
using key_conv = make_caster<Key>;
bool load(handle src, bool convert) {
pybind11::set s(src, true);
@ -57,9 +57,9 @@ template <typename Type, typename Key> struct set_caster {
};
template <typename Type, typename Key, typename Value> struct map_caster {
typedef Type type;
typedef type_caster<typename intrinsic_type<Key>::type> key_conv;
typedef type_caster<typename intrinsic_type<Value>::type> value_conv;
using type = Type;
using key_conv = make_caster<Key>;
using value_conv = make_caster<Value>;
bool load(handle src, bool convert) {
dict d(src, true);
@ -93,8 +93,8 @@ template <typename Type, typename Key, typename Value> struct map_caster {
};
template <typename Type, typename Value> struct list_caster {
typedef Type type;
typedef type_caster<typename intrinsic_type<Value>::type> value_conv;
using type = Type;
using value_conv = make_caster<Value>;
bool load(handle src, bool convert) {
list l(src, true);
@ -138,8 +138,8 @@ template <typename Type, typename Alloc> struct type_caster<std::list<Type, Allo
: list_caster<std::list<Type, Alloc>, Type> { };
template <typename Type, size_t Size> struct type_caster<std::array<Type, Size>> {
typedef std::array<Type, Size> array_type;
typedef type_caster<typename intrinsic_type<Type>::type> value_conv;
using array_type = std::array<Type, Size>;
using value_conv = make_caster<Type>;
bool load(handle src, bool convert) {
list l(src, true);

View File

@ -68,18 +68,22 @@ class Capture(object):
def __init__(self, capfd):
self.capfd = capfd
self.out = ""
self.err = ""
def _flush_stdout(self):
def _flush(self):
"""Workaround for issues on Windows: to be removed after tests get py::print"""
sys.stdout.flush()
os.fsync(sys.stdout.fileno()) # make sure C++ output is also read
return self.capfd.readouterr()[0]
os.fsync(sys.stdout.fileno())
sys.stderr.flush()
os.fsync(sys.stderr.fileno())
return self.capfd.readouterr()
def __enter__(self):
self._flush_stdout()
self._flush()
return self
def __exit__(self, *_):
self.out = self._flush_stdout()
self.out, self.err = self._flush()
def __eq__(self, other):
a = Output(self.out)
@ -100,6 +104,10 @@ class Capture(object):
def unordered(self):
return Unordered(self.out)
@property
def stderr(self):
return Output(self.err)
@pytest.fixture
def capture(capfd):

View File

@ -8,6 +8,7 @@ using std::cout;
using std::endl;
namespace py = pybind11;
using namespace pybind11::literals;
class test_initializer {
public:

View File

@ -71,6 +71,9 @@ struct Payload {
}
};
/// Something to trigger a conversion error
struct Unregistered {};
test_initializer callbacks([](py::module &m) {
m.def("test_callback1", &test_callback1);
m.def("test_callback2", &test_callback2);
@ -78,8 +81,56 @@ test_initializer callbacks([](py::module &m) {
m.def("test_callback4", &test_callback4);
m.def("test_callback5", &test_callback5);
/* Test cleanup of lambda closure */
// Test keyword args and generalized unpacking
m.def("test_tuple_unpacking", [](py::function f) {
auto t1 = py::make_tuple(2, 3);
auto t2 = py::make_tuple(5, 6);
return f("positional", 1, *t1, 4, *t2);
});
m.def("test_dict_unpacking", [](py::function f) {
auto d1 = py::dict("key"_a="value", "a"_a=1);
auto d2 = py::dict();
auto d3 = py::dict("b"_a=2);
return f("positional", 1, **d1, **d2, **d3);
});
m.def("test_keyword_args", [](py::function f) {
return f("x"_a=10, "y"_a=20);
});
m.def("test_unpacking_and_keywords1", [](py::function f) {
auto args = py::make_tuple(2);
auto kwargs = py::dict("d"_a=4);
return f(1, *args, "c"_a=3, **kwargs);
});
m.def("test_unpacking_and_keywords2", [](py::function f) {
auto kwargs1 = py::dict("a"_a=1);
auto kwargs2 = py::dict("c"_a=3, "d"_a=4);
return f("positional", *py::make_tuple(1), 2, *py::make_tuple(3, 4), 5,
"key"_a="value", **kwargs1, "b"_a=2, **kwargs2, "e"_a=5);
});
m.def("test_unpacking_error1", [](py::function f) {
auto kwargs = py::dict("x"_a=3);
return f("x"_a=1, "y"_a=2, **kwargs); // duplicate ** after keyword
});
m.def("test_unpacking_error2", [](py::function f) {
auto kwargs = py::dict("x"_a=3);
return f(**kwargs, "x"_a=1); // duplicate keyword after **
});
m.def("test_arg_conversion_error1", [](py::function f) {
f(234, Unregistered(), "kw"_a=567);
});
m.def("test_arg_conversion_error2", [](py::function f) {
f(234, "expected_name"_a=Unregistered(), "kw"_a=567);
});
/* Test cleanup of lambda closure */
m.def("test_cleanup", []() -> std::function<void(void)> {
Payload p;

View File

@ -27,6 +27,41 @@ def test_callbacks():
assert f(number=43) == 44
def test_keyword_args_and_generalized_unpacking():
from pybind11_tests import (test_tuple_unpacking, test_dict_unpacking, test_keyword_args,
test_unpacking_and_keywords1, test_unpacking_and_keywords2,
test_unpacking_error1, test_unpacking_error2,
test_arg_conversion_error1, test_arg_conversion_error2)
def f(*args, **kwargs):
return args, kwargs
assert test_tuple_unpacking(f) == (("positional", 1, 2, 3, 4, 5, 6), {})
assert test_dict_unpacking(f) == (("positional", 1), {"key": "value", "a": 1, "b": 2})
assert test_keyword_args(f) == ((), {"x": 10, "y": 20})
assert test_unpacking_and_keywords1(f) == ((1, 2), {"c": 3, "d": 4})
assert test_unpacking_and_keywords2(f) == (
("positional", 1, 2, 3, 4, 5),
{"key": "value", "a": 1, "b": 2, "c": 3, "d": 4, "e": 5}
)
with pytest.raises(TypeError) as excinfo:
test_unpacking_error1(f)
assert "Got multiple values for keyword argument" in str(excinfo.value)
with pytest.raises(TypeError) as excinfo:
test_unpacking_error2(f)
assert "Got multiple values for keyword argument" in str(excinfo.value)
with pytest.raises(RuntimeError) as excinfo:
test_arg_conversion_error1(f)
assert "Unable to convert call argument" in str(excinfo.value)
with pytest.raises(RuntimeError) as excinfo:
test_arg_conversion_error2(f)
assert "Unable to convert call argument" in str(excinfo.value)
def test_lambda_closure_cleanup():
from pybind11_tests import test_cleanup, payload_cstats

View File

@ -20,13 +20,6 @@ std::string kw_func4(const std::vector<int> &entries) {
return ret;
}
py::object call_kw_func(py::function f) {
py::tuple args = py::make_tuple(1234);
py::dict kwargs;
kwargs["y"] = py::cast(5678);
return f(*args, **kwargs);
}
py::tuple args_function(py::args args) {
return args;
}
@ -49,14 +42,11 @@ test_initializer arg_keywords_and_defaults([](py::module &m) {
std::vector<int> list;
list.push_back(13);
list.push_back(17);
m.def("kw_func4", &kw_func4, py::arg("myList") = list);
m.def("call_kw_func", &call_kw_func);
m.def("args_function", &args_function);
m.def("args_kwargs_function", &args_kwargs_function);
using namespace py::literals;
m.def("kw_func_udl", &kw_func, "x"_a, "y"_a=300);
m.def("kw_func_udl_z", &kw_func, "x"_a, "y"_a=0);

View File

@ -1,7 +1,6 @@
import pytest
from pybind11_tests import (kw_func0, kw_func1, kw_func2, kw_func3, kw_func4, call_kw_func,
args_function, args_kwargs_function, kw_func_udl, kw_func_udl_z,
KWClass)
from pybind11_tests import (kw_func0, kw_func1, kw_func2, kw_func3, kw_func4, args_function,
args_kwargs_function, kw_func_udl, kw_func_udl_z, KWClass)
def test_function_signatures(doc):
@ -49,8 +48,6 @@ def test_named_arguments(msg):
def test_arg_and_kwargs():
assert call_kw_func(kw_func2) == "x=1234, y=5678"
args = 'arg1_value', 'arg2_value', 3
assert args_function(*args) == args

View File

@ -197,4 +197,32 @@ test_initializer python_types([](py::module &m) {
.def_readwrite_static("value", &ExamplePythonTypes::value, "Static value member")
.def_readonly_static("value2", &ExamplePythonTypes::value2, "Static value member (readonly)")
;
m.def("test_print_function", []() {
py::print("Hello, World!");
py::print(1, 2.0, "three", true, std::string("-- multiple args"));
auto args = py::make_tuple("and", "a", "custom", "separator");
py::print("*args", *args, "sep"_a="-");
py::print("no new line here", "end"_a=" -- ");
py::print("next print");
auto py_stderr = py::module::import("sys").attr("stderr").cast<py::object>();
py::print("this goes to stderr", "file"_a=py_stderr);
py::print("flush", "flush"_a=true);
py::print("{a} + {b} = {c}"_s.format("a"_a="py::print", "b"_a="str.format", "c"_a="this"));
});
m.def("test_str_format", []() {
auto s1 = "{} + {} = {}"_s.format(1, 2, 3);
auto s2 = "{a} + {b} = {c}"_s.format("a"_a=1, "b"_a=2, "c"_a=3);
return py::make_tuple(s1, s2);
});
m.def("test_dict_keyword_constructor", []() {
auto d1 = py::dict("x"_a=1, "y"_a=2);
auto d2 = py::dict("z"_a=3, **d1);
return d2;
});
});

View File

@ -218,3 +218,33 @@ def test_module():
assert ExamplePythonTypes.__module__ == "pybind11_tests"
assert ExamplePythonTypes.get_set.__name__ == "get_set"
assert ExamplePythonTypes.get_set.__module__ == "pybind11_tests"
def test_print(capture):
from pybind11_tests import test_print_function
with capture:
test_print_function()
assert capture == """
Hello, World!
1 2.0 three True -- multiple args
*args-and-a-custom-separator
no new line here -- next print
flush
py::print + str.format = this
"""
assert capture.stderr == "this goes to stderr"
def test_str_api():
from pybind11_tests import test_str_format
s1, s2 = test_str_format()
assert s1 == "1 + 2 = 3"
assert s1 == s2
def test_dict_api():
from pybind11_tests import test_dict_keyword_constructor
assert test_dict_keyword_constructor() == {"x": 1, "y": 2, "z": 3}