16 #ifndef BOOST_ADAPTBX_PYTHON_STREAMBUF_H
17 #define BOOST_ADAPTBX_PYTHON_STREAMBUF_H
19 #include <boost/python/object.hpp>
20 #include <boost/python/str.hpp>
21 #include <boost/python/extract.hpp>
23 #include <boost/optional.hpp>
24 #include <boost/utility/typed_in_place_factory.hpp>
37 namespace bp = boost::python;
114 typedef std::basic_streambuf<char> base_t;
139 streambuf(bp::object& python_file_obj, std::size_t buffer_size_ = 0)
140 : py_read(getattr(python_file_obj,
"read", bp::object())),
141 py_write(getattr(python_file_obj,
"write", bp::object())),
142 py_seek(getattr(python_file_obj,
"seek", bp::object())),
143 py_tell(getattr(python_file_obj,
"tell", bp::object())),
146 pos_of_read_buffer_end_in_py_file(0),
147 pos_of_write_buffer_end_in_py_file(buffer_size),
154 if (py_tell != bp::object()) {
156 off_type py_pos = bp::extract<off_type>(py_tell());
157 if (py_seek != bp::object()) {
164 }
catch (bp::error_already_set&) {
165 py_tell = bp::object();
166 py_seek = bp::object();
174 if (py_write != bp::object()) {
176 write_buffer =
new char[buffer_size + 1];
177 write_buffer[buffer_size] =
'\0';
178 setp(write_buffer, write_buffer + buffer_size);
179 farthest_pptr = pptr();
185 if (py_tell != bp::object()) {
186 off_type py_pos = bp::extract<off_type>(py_tell());
187 pos_of_read_buffer_end_in_py_file = py_pos;
188 pos_of_write_buffer_end_in_py_file = py_pos;
194 std::size_t buffer_size_ = 0)
195 :
streambuf(python_file_obj, buffer_size_) {
197 bp::object io_mod = bp::import(
"io");
199 bp::object iobase = io_mod.attr(
"TextIOBase");;
206 static bp::object io_mod = bp::object();
207 static bp::object iobase = bp::object();
208 if(!io_mod) io_mod = bp::import(
"io");
209 if(io_mod && !iobase) iobase = io_mod.attr(
"TextIOBase");
214 bool isTextMode = PyObject_IsInstance(python_file_obj.ptr(), iobase.ptr());
220 "Need a text mode file object like StringIO or a file opened "
226 "Need a binary mode file object like BytesIO or a file opened "
230 throw std::invalid_argument(
"bad mode character");
236 if (write_buffer)
delete[] write_buffer;
244 int_type const failure = traits_type::eof();
246 if (status == failure)
return -1;
247 return egptr() - gptr();
252 int_type const failure = traits_type::eof();
253 if (py_read == bp::object()) {
254 throw std::invalid_argument(
255 "That Python file object has no 'read' attribute");
257 read_buffer = py_read(buffer_size);
258 char* read_buffer_data;
259 bp::ssize_t py_n_read;
260 if (PyBytes_AsStringAndSize(read_buffer.ptr(), &read_buffer_data,
263 throw std::invalid_argument(
264 "The method 'read' of the Python file object "
265 "did not return a string.");
268 pos_of_read_buffer_end_in_py_file += n_read;
269 setg(read_buffer_data, read_buffer_data, read_buffer_data + n_read);
271 if (n_read == 0)
return failure;
272 return traits_type::to_int_type(read_buffer_data[0]);
277 if (py_write == bp::object()) {
278 throw std::invalid_argument(
279 "That Python file object has no 'write' attribute");
281 farthest_pptr = std::max(farthest_pptr, pptr());
283 bp::str chunk(pbase(), farthest_pptr);
285 if (!traits_type::eq_int_type(c, traits_type::eof())) {
286 py_write(traits_type::to_char_type(c));
290 pos_of_write_buffer_end_in_py_file += n_written;
291 setp(pbase(), epptr());
293 farthest_pptr = pptr();
295 return traits_type::eq_int_type(c, traits_type::eof())
296 ? traits_type::not_eof(c)
309 farthest_pptr = std::max(farthest_pptr, pptr());
310 if (farthest_pptr && farthest_pptr > pbase()) {
311 off_type delta = pptr() - farthest_pptr;
313 if (traits_type::eq_int_type(status, traits_type::eof())) result = -1;
314 if (py_seek != bp::object()) py_seek(delta, 1);
315 }
else if (gptr() && gptr() < egptr()) {
316 if (py_seek != bp::object()) py_seek(gptr() - egptr(), 1);
329 std::ios_base::openmode which = std::ios_base::in |
330 std::ios_base::out) {
338 if (py_seek == bp::object()) {
339 throw std::invalid_argument(
340 "That Python file object has no 'seek' attribute");
344 if (which == std::ios_base::in && !gptr()) {
345 if (traits_type::eq_int_type(
underflow(), traits_type::eof())) {
353 case std::ios_base::beg:
356 case std::ios_base::cur:
359 case std::ios_base::end:
367 boost::optional<off_type> result =
368 seekoff_without_calling_python(off, way, which);
371 if (which == std::ios_base::out)
overflow();
372 if (way == std::ios_base::cur) {
373 if (which == std::ios_base::in)
374 off -= egptr() - gptr();
375 else if (which == std::ios_base::out)
376 off += pptr() - pbase();
378 py_seek(off, whence);
379 result =
off_type(bp::extract<off_type>(py_tell()));
380 if (which == std::ios_base::in)
underflow();
387 std::ios_base::openmode which = std::ios_base::in |
388 std::ios_base::out) {
393 bp::object py_read, py_write, py_seek, py_tell;
395 std::size_t buffer_size;
402 bp::object read_buffer;
409 off_type pos_of_read_buffer_end_in_py_file,
410 pos_of_write_buffer_end_in_py_file;
415 boost::optional<off_type> seekoff_without_calling_python(
416 off_type off, std::ios_base::seekdir way, std::ios_base::openmode which) {
417 boost::optional<off_type>
const failure;
420 off_type buf_begin, buf_end, buf_cur, upper_bound;
421 off_type pos_of_buffer_end_in_py_file;
422 if (which == std::ios_base::in) {
423 pos_of_buffer_end_in_py_file = pos_of_read_buffer_end_in_py_file;
424 buf_begin =
reinterpret_cast<std::streamsize
>(eback());
425 buf_cur =
reinterpret_cast<std::streamsize
>(gptr());
426 buf_end =
reinterpret_cast<std::streamsize
>(egptr());
427 upper_bound = buf_end;
428 }
else if (which == std::ios_base::out) {
429 pos_of_buffer_end_in_py_file = pos_of_write_buffer_end_in_py_file;
430 buf_begin =
reinterpret_cast<std::streamsize
>(pbase());
431 buf_cur =
reinterpret_cast<std::streamsize
>(pptr());
432 buf_end =
reinterpret_cast<std::streamsize
>(epptr());
433 farthest_pptr = std::max(farthest_pptr, pptr());
434 upper_bound =
reinterpret_cast<std::streamsize
>(farthest_pptr) + 1;
441 if (way == std::ios_base::cur) {
442 buf_sought = buf_cur + off;
443 }
else if (way == std::ios_base::beg) {
444 buf_sought = buf_end + (off - pos_of_buffer_end_in_py_file);
445 }
else if (way == std::ios_base::end) {
452 if (buf_sought < buf_begin || buf_sought >= upper_bound)
return failure;
455 if (which == std::ios_base::in)
456 gbump(buf_sought - buf_cur);
457 else if (which == std::ios_base::out)
458 pbump(buf_sought - buf_cur);
459 return pos_of_buffer_end_in_py_file + (buf_sought - buf_end);
466 exceptions(std::ios_base::badbit);
482 exceptions(std::ios_base::badbit);
486 if (this->good()) this->flush();
501 ostream(bp::object& python_file_obj, std::size_t buffer_size = 0)
507 if (this->good()) this->flush();
508 }
catch (bp::error_already_set&) {
510 throw std::runtime_error(
511 "Problem closing python ostream.\n"
512 " Known limitation: the error is unrecoverable. Sorry.\n"
513 " Suggestion for programmer: add ostream.flush() before"