mirror of
https://github.com/pybind/pybind11.git
synced 2025-02-16 21:57:55 +00:00
Add py::print() function
Replicates Python API including keyword arguments.
This commit is contained in:
parent
c743e1b1b4
commit
67990d9e19
@ -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)
|
#if defined(WITH_THREAD)
|
||||||
|
|
||||||
/* The functions below essentially reproduce the PyGILState_* API using a RAII
|
/* The functions below essentially reproduce the PyGILState_* API using a RAII
|
||||||
|
@ -68,18 +68,22 @@ class Capture(object):
|
|||||||
def __init__(self, capfd):
|
def __init__(self, capfd):
|
||||||
self.capfd = capfd
|
self.capfd = capfd
|
||||||
self.out = ""
|
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()
|
sys.stdout.flush()
|
||||||
os.fsync(sys.stdout.fileno()) # make sure C++ output is also read
|
os.fsync(sys.stdout.fileno())
|
||||||
return self.capfd.readouterr()[0]
|
sys.stderr.flush()
|
||||||
|
os.fsync(sys.stderr.fileno())
|
||||||
|
return self.capfd.readouterr()
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self._flush_stdout()
|
self._flush()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, *_):
|
def __exit__(self, *_):
|
||||||
self.out = self._flush_stdout()
|
self.out, self.err = self._flush()
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
a = Output(self.out)
|
a = Output(self.out)
|
||||||
@ -100,6 +104,10 @@ class Capture(object):
|
|||||||
def unordered(self):
|
def unordered(self):
|
||||||
return Unordered(self.out)
|
return Unordered(self.out)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def stderr(self):
|
||||||
|
return Output(self.err)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def capture(capfd):
|
def capture(capfd):
|
||||||
|
@ -197,4 +197,18 @@ test_initializer python_types([](py::module &m) {
|
|||||||
.def_readwrite_static("value", &ExamplePythonTypes::value, "Static value member")
|
.def_readwrite_static("value", &ExamplePythonTypes::value, "Static value member")
|
||||||
.def_readonly_static("value2", &ExamplePythonTypes::value2, "Static value member (readonly)")
|
.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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -218,3 +218,18 @@ def test_module():
|
|||||||
assert ExamplePythonTypes.__module__ == "pybind11_tests"
|
assert ExamplePythonTypes.__module__ == "pybind11_tests"
|
||||||
assert ExamplePythonTypes.get_set.__name__ == "get_set"
|
assert ExamplePythonTypes.get_set.__name__ == "get_set"
|
||||||
assert ExamplePythonTypes.get_set.__module__ == "pybind11_tests"
|
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
|
||||||
|
"""
|
||||||
|
assert capture.stderr == "this goes to stderr"
|
||||||
|
Loading…
Reference in New Issue
Block a user