Blame external/pybind11/include/pybind11/iostream.h

Packit 534379
/*
Packit 534379
    pybind11/iostream.h -- Tools to assist with redirecting cout and cerr to Python
Packit 534379
Packit 534379
    Copyright (c) 2017 Henry F. Schreiner
Packit 534379
Packit 534379
    All rights reserved. Use of this source code is governed by a
Packit 534379
    BSD-style license that can be found in the LICENSE file.
Packit 534379
*/
Packit 534379
Packit 534379
#pragma once
Packit 534379
Packit 534379
#include "pybind11.h"
Packit 534379
Packit 534379
#include <streambuf>
Packit 534379
#include <ostream>
Packit 534379
#include <string>
Packit 534379
#include <memory>
Packit 534379
#include <iostream>
Packit 534379
Packit 534379
NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
Packit 534379
NAMESPACE_BEGIN(detail)
Packit 534379
Packit 534379
// Buffer that writes to Python instead of C++
Packit 534379
class pythonbuf : public std::streambuf {
Packit 534379
private:
Packit 534379
    using traits_type = std::streambuf::traits_type;
Packit 534379
Packit 534379
    const size_t buf_size;
Packit 534379
    std::unique_ptr<char[]> d_buffer;
Packit 534379
    object pywrite;
Packit 534379
    object pyflush;
Packit 534379
Packit 534379
    int overflow(int c) {
Packit 534379
        if (!traits_type::eq_int_type(c, traits_type::eof())) {
Packit 534379
            *pptr() = traits_type::to_char_type(c);
Packit 534379
            pbump(1);
Packit 534379
        }
Packit 534379
        return sync() == 0 ? traits_type::not_eof(c) : traits_type::eof();
Packit 534379
    }
Packit 534379
Packit 534379
    int sync() {
Packit 534379
        if (pbase() != pptr()) {
Packit 534379
            // This subtraction cannot be negative, so dropping the sign
Packit 534379
            str line(pbase(), static_cast<size_t>(pptr() - pbase()));
Packit 534379
Packit 534379
            {
Packit 534379
                gil_scoped_acquire tmp;
Packit 534379
                pywrite(line);
Packit 534379
                pyflush();
Packit 534379
            }
Packit 534379
Packit 534379
            setp(pbase(), epptr());
Packit 534379
        }
Packit 534379
        return 0;
Packit 534379
    }
Packit 534379
Packit 534379
public:
Packit 534379
Packit 534379
    pythonbuf(object pyostream, size_t buffer_size = 1024)
Packit 534379
        : buf_size(buffer_size),
Packit 534379
          d_buffer(new char[buf_size]),
Packit 534379
          pywrite(pyostream.attr("write")),
Packit 534379
          pyflush(pyostream.attr("flush")) {
Packit 534379
        setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
Packit 534379
    }
Packit 534379
Packit 534379
    pythonbuf(pythonbuf&&) = default;
Packit 534379
Packit 534379
    /// Sync before destroy
Packit 534379
    ~pythonbuf() {
Packit 534379
        sync();
Packit 534379
    }
Packit 534379
};
Packit 534379
Packit 534379
NAMESPACE_END(detail)
Packit 534379
Packit 534379
Packit 534379
/** \rst
Packit 534379
    This a move-only guard that redirects output.
Packit 534379
Packit 534379
    .. code-block:: cpp
Packit 534379
Packit 534379
        #include <pybind11/iostream.h>
Packit 534379
Packit 534379
        ...
Packit 534379
Packit 534379
        {
Packit 534379
            py::scoped_ostream_redirect output;
Packit 534379
            std::cout << "Hello, World!"; // Python stdout
Packit 534379
        } // <-- return std::cout to normal
Packit 534379
Packit 534379
    You can explicitly pass the c++ stream and the python object,
Packit 534379
    for example to guard stderr instead.
Packit 534379
Packit 534379
    .. code-block:: cpp
Packit 534379
Packit 534379
        {
Packit 534379
            py::scoped_ostream_redirect output{std::cerr, py::module::import("sys").attr("stderr")};
Packit 534379
            std::cerr << "Hello, World!";
Packit 534379
        }
Packit 534379
 \endrst */
Packit 534379
class scoped_ostream_redirect {
Packit 534379
protected:
Packit 534379
    std::streambuf *old;
Packit 534379
    std::ostream &costream;
Packit 534379
    detail::pythonbuf buffer;
Packit 534379
Packit 534379
public:
Packit 534379
    scoped_ostream_redirect(
Packit 534379
            std::ostream &costream = std::cout,
Packit 534379
            object pyostream = module::import("sys").attr("stdout"))
Packit 534379
        : costream(costream), buffer(pyostream) {
Packit 534379
        old = costream.rdbuf(&buffer);
Packit 534379
    }
Packit 534379
Packit 534379
    ~scoped_ostream_redirect() {
Packit 534379
        costream.rdbuf(old);
Packit 534379
    }
Packit 534379
Packit 534379
    scoped_ostream_redirect(const scoped_ostream_redirect &) = delete;
Packit 534379
    scoped_ostream_redirect(scoped_ostream_redirect &&other) = default;
Packit 534379
    scoped_ostream_redirect &operator=(const scoped_ostream_redirect &) = delete;
Packit 534379
    scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete;
Packit 534379
};
Packit 534379
Packit 534379
Packit 534379
/** \rst
Packit 534379
    Like `scoped_ostream_redirect`, but redirects cerr by default. This class
Packit 534379
    is provided primary to make ``py::call_guard`` easier to make.
Packit 534379
Packit 534379
    .. code-block:: cpp
Packit 534379
Packit 534379
     m.def("noisy_func", &noisy_func,
Packit 534379
           py::call_guard
Packit 534379
                          scoped_estream_redirect>());
Packit 534379
Packit 534379
\endrst */
Packit 534379
class scoped_estream_redirect : public scoped_ostream_redirect {
Packit 534379
public:
Packit 534379
    scoped_estream_redirect(
Packit 534379
            std::ostream &costream = std::cerr,
Packit 534379
            object pyostream = module::import("sys").attr("stderr"))
Packit 534379
        : scoped_ostream_redirect(costream,pyostream) {}
Packit 534379
};
Packit 534379
Packit 534379
Packit 534379
NAMESPACE_BEGIN(detail)
Packit 534379
Packit 534379
// Class to redirect output as a context manager. C++ backend.
Packit 534379
class OstreamRedirect {
Packit 534379
    bool do_stdout_;
Packit 534379
    bool do_stderr_;
Packit 534379
    std::unique_ptr<scoped_ostream_redirect> redirect_stdout;
Packit 534379
    std::unique_ptr<scoped_estream_redirect> redirect_stderr;
Packit 534379
Packit 534379
public:
Packit 534379
    OstreamRedirect(bool do_stdout = true, bool do_stderr = true)
Packit 534379
        : do_stdout_(do_stdout), do_stderr_(do_stderr) {}
Packit 534379
Packit 534379
    void enter() {
Packit 534379
        if (do_stdout_)
Packit 534379
            redirect_stdout.reset(new scoped_ostream_redirect());
Packit 534379
        if (do_stderr_)
Packit 534379
            redirect_stderr.reset(new scoped_estream_redirect());
Packit 534379
    }
Packit 534379
Packit 534379
    void exit() {
Packit 534379
        redirect_stdout.reset();
Packit 534379
        redirect_stderr.reset();
Packit 534379
    }
Packit 534379
};
Packit 534379
Packit 534379
NAMESPACE_END(detail)
Packit 534379
Packit 534379
/** \rst
Packit 534379
    This is a helper function to add a C++ redirect context manager to Python
Packit 534379
    instead of using a C++ guard. To use it, add the following to your binding code:
Packit 534379
Packit 534379
    .. code-block:: cpp
Packit 534379
Packit 534379
        #include <pybind11/iostream.h>
Packit 534379
Packit 534379
        ...
Packit 534379
Packit 534379
        py::add_ostream_redirect(m, "ostream_redirect");
Packit 534379
Packit 534379
    You now have a Python context manager that redirects your output:
Packit 534379
Packit 534379
    .. code-block:: python
Packit 534379
Packit 534379
        with m.ostream_redirect():
Packit 534379
            m.print_to_cout_function()
Packit 534379
Packit 534379
    This manager can optionally be told which streams to operate on:
Packit 534379
Packit 534379
    .. code-block:: python
Packit 534379
Packit 534379
        with m.ostream_redirect(stdout=true, stderr=true):
Packit 534379
            m.noisy_function_with_error_printing()
Packit 534379
Packit 534379
 \endrst */
Packit 534379
inline class_<detail::OstreamRedirect> add_ostream_redirect(module m, std::string name = "ostream_redirect") {
Packit 534379
    return class_<detail::OstreamRedirect>(m, name.c_str(), module_local())
Packit 534379
        .def(init<bool,bool>(), arg("stdout")=true, arg("stderr")=true)
Packit 534379
        .def("__enter__", &detail::OstreamRedirect::enter)
Packit 534379
        .def("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); });
Packit 534379
}
Packit 534379
Packit 534379
NAMESPACE_END(PYBIND11_NAMESPACE)