File indexing completed on 2025-01-18 10:06:14
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020 #pragma once
0021
0022 #include "pybind11.h"
0023
0024 #include <algorithm>
0025 #include <cstring>
0026 #include <iostream>
0027 #include <iterator>
0028 #include <memory>
0029 #include <ostream>
0030 #include <streambuf>
0031 #include <string>
0032 #include <utility>
0033
0034 PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
0035 PYBIND11_NAMESPACE_BEGIN(detail)
0036
0037
0038 class pythonbuf : public std::streambuf {
0039 private:
0040 using traits_type = std::streambuf::traits_type;
0041
0042 const size_t buf_size;
0043 std::unique_ptr<char[]> d_buffer;
0044 object pywrite;
0045 object pyflush;
0046
0047 int overflow(int c) override {
0048 if (!traits_type::eq_int_type(c, traits_type::eof())) {
0049 *pptr() = traits_type::to_char_type(c);
0050 pbump(1);
0051 }
0052 return sync() == 0 ? traits_type::not_eof(c) : traits_type::eof();
0053 }
0054
0055
0056
0057
0058 size_t utf8_remainder() const {
0059 const auto rbase = std::reverse_iterator<char *>(pbase());
0060 const auto rpptr = std::reverse_iterator<char *>(pptr());
0061 auto is_ascii = [](char c) { return (static_cast<unsigned char>(c) & 0x80) == 0x00; };
0062 auto is_leading = [](char c) { return (static_cast<unsigned char>(c) & 0xC0) == 0xC0; };
0063 auto is_leading_2b = [](char c) { return static_cast<unsigned char>(c) <= 0xDF; };
0064 auto is_leading_3b = [](char c) { return static_cast<unsigned char>(c) <= 0xEF; };
0065
0066 if (is_ascii(*rpptr)) {
0067 return 0;
0068 }
0069
0070
0071 const auto rpend = rbase - rpptr >= 3 ? rpptr + 3 : rbase;
0072 const auto leading = std::find_if(rpptr, rpend, is_leading);
0073 if (leading == rbase) {
0074 return 0;
0075 }
0076 const auto dist = static_cast<size_t>(leading - rpptr);
0077 size_t remainder = 0;
0078
0079 if (dist == 0) {
0080 remainder = 1;
0081 } else if (dist == 1) {
0082 remainder = is_leading_2b(*leading) ? 0 : dist + 1;
0083 } else if (dist == 2) {
0084 remainder = is_leading_3b(*leading) ? 0 : dist + 1;
0085 }
0086
0087
0088
0089
0090 return remainder;
0091 }
0092
0093
0094 int _sync() {
0095 if (pbase() != pptr()) {
0096 gil_scoped_acquire tmp;
0097
0098 auto size = static_cast<size_t>(pptr() - pbase());
0099 size_t remainder = utf8_remainder();
0100
0101 if (size > remainder) {
0102 str line(pbase(), size - remainder);
0103 pywrite(std::move(line));
0104 pyflush();
0105 }
0106
0107
0108 if (remainder > 0) {
0109 std::memmove(pbase(), pptr() - remainder, remainder);
0110 }
0111 setp(pbase(), epptr());
0112 pbump(static_cast<int>(remainder));
0113 }
0114 return 0;
0115 }
0116
0117 int sync() override { return _sync(); }
0118
0119 public:
0120 explicit pythonbuf(const object &pyostream, size_t buffer_size = 1024)
0121 : buf_size(buffer_size), d_buffer(new char[buf_size]), pywrite(pyostream.attr("write")),
0122 pyflush(pyostream.attr("flush")) {
0123 setp(d_buffer.get(), d_buffer.get() + buf_size - 1);
0124 }
0125
0126 pythonbuf(pythonbuf &&) = default;
0127
0128
0129 ~pythonbuf() override { _sync(); }
0130 };
0131
0132 PYBIND11_NAMESPACE_END(detail)
0133
0134
0135
0136
0137
0138
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159 class scoped_ostream_redirect {
0160 protected:
0161 std::streambuf *old;
0162 std::ostream &costream;
0163 detail::pythonbuf buffer;
0164
0165 public:
0166 explicit scoped_ostream_redirect(std::ostream &costream = std::cout,
0167 const object &pyostream
0168 = module_::import("sys").attr("stdout"))
0169 : costream(costream), buffer(pyostream) {
0170 old = costream.rdbuf(&buffer);
0171 }
0172
0173 ~scoped_ostream_redirect() { costream.rdbuf(old); }
0174
0175 scoped_ostream_redirect(const scoped_ostream_redirect &) = delete;
0176 scoped_ostream_redirect(scoped_ostream_redirect &&other) = default;
0177 scoped_ostream_redirect &operator=(const scoped_ostream_redirect &) = delete;
0178 scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete;
0179 };
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189
0190
0191
0192 class scoped_estream_redirect : public scoped_ostream_redirect {
0193 public:
0194 explicit scoped_estream_redirect(std::ostream &costream = std::cerr,
0195 const object &pyostream
0196 = module_::import("sys").attr("stderr"))
0197 : scoped_ostream_redirect(costream, pyostream) {}
0198 };
0199
0200 PYBIND11_NAMESPACE_BEGIN(detail)
0201
0202
0203 class OstreamRedirect {
0204 bool do_stdout_;
0205 bool do_stderr_;
0206 std::unique_ptr<scoped_ostream_redirect> redirect_stdout;
0207 std::unique_ptr<scoped_estream_redirect> redirect_stderr;
0208
0209 public:
0210 explicit OstreamRedirect(bool do_stdout = true, bool do_stderr = true)
0211 : do_stdout_(do_stdout), do_stderr_(do_stderr) {}
0212
0213 void enter() {
0214 if (do_stdout_) {
0215 redirect_stdout.reset(new scoped_ostream_redirect());
0216 }
0217 if (do_stderr_) {
0218 redirect_stderr.reset(new scoped_estream_redirect());
0219 }
0220 }
0221
0222 void exit() {
0223 redirect_stdout.reset();
0224 redirect_stderr.reset();
0225 }
0226 };
0227
0228 PYBIND11_NAMESPACE_END(detail)
0229
0230
0231
0232
0233
0234
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244
0245
0246
0247
0248
0249
0250
0251
0252
0253
0254
0255
0256
0257 inline class_<detail::OstreamRedirect>
0258 add_ostream_redirect(module_ m, const std::string &name = "ostream_redirect") {
0259 return class_<detail::OstreamRedirect>(std::move(m), name.c_str(), module_local())
0260 .def(init<bool, bool>(), arg("stdout") = true, arg("stderr") = true)
0261 .def("__enter__", &detail::OstreamRedirect::enter)
0262 .def("__exit__", [](detail::OstreamRedirect &self_, const args &) { self_.exit(); });
0263 }
0264
0265 PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)