15 #ifndef BOOST_ADAPTBX_PYTHON_STREAMBUF_H 16 #define BOOST_ADAPTBX_PYTHON_STREAMBUF_H 18 #include <boost/python/object.hpp> 19 #include <boost/python/str.hpp> 20 #include <boost/python/extract.hpp> 22 #include <boost/optional.hpp> 23 #include <boost/utility/typed_in_place_factory.hpp> 35 namespace bp = boost::python;
112 typedef std::basic_streambuf<char> base_t;
137 streambuf(bp::object& python_file_obj, std::size_t buffer_size_ = 0)
138 : py_read(getattr(python_file_obj,
"read", bp::object())),
139 py_write(getattr(python_file_obj,
"write", bp::object())),
140 py_seek(getattr(python_file_obj,
"seek", bp::object())),
141 py_tell(getattr(python_file_obj,
"tell", bp::object())),
142 buffer_size(buffer_size_ != 0 ? buffer_size_ : default_buffer_size),
144 pos_of_read_buffer_end_in_py_file(0),
145 pos_of_write_buffer_end_in_py_file(buffer_size),
152 if (py_tell != bp::object()) {
154 off_type py_pos = bp::extract<off_type>(py_tell());
155 if (py_seek != bp::object()) {
162 }
catch (bp::error_already_set&) {
163 py_tell = bp::object();
164 py_seek = bp::object();
172 if (py_write != bp::object()) {
174 write_buffer =
new char[buffer_size + 1];
175 write_buffer[buffer_size] =
'\0';
176 setp(write_buffer, write_buffer + buffer_size);
177 farthest_pptr = pptr();
183 if (py_tell != bp::object()) {
184 off_type py_pos = bp::extract<off_type>(py_tell());
185 pos_of_read_buffer_end_in_py_file = py_pos;
186 pos_of_write_buffer_end_in_py_file = py_pos;
192 if (write_buffer)
delete[] write_buffer;
200 int_type
const failure = traits_type::eof();
202 if (status == failure)
return -1;
203 return egptr() - gptr();
208 int_type
const failure = traits_type::eof();
209 if (py_read == bp::object()) {
210 throw std::invalid_argument(
211 "That Python file object has no 'read' attribute");
213 read_buffer = py_read(buffer_size);
214 char* read_buffer_data;
215 bp::ssize_t py_n_read;
216 if (PyBytes_AsStringAndSize(read_buffer.ptr(), &read_buffer_data,
219 throw std::invalid_argument(
220 "The method 'read' of the Python file object " 221 "did not return a string.");
223 off_type n_read = (
off_type)py_n_read;
224 pos_of_read_buffer_end_in_py_file += n_read;
225 setg(read_buffer_data, read_buffer_data, read_buffer_data + n_read);
227 if (n_read == 0)
return failure;
228 return traits_type::to_int_type(read_buffer_data[0]);
233 if (py_write == bp::object()) {
234 throw std::invalid_argument(
235 "That Python file object has no 'write' attribute");
237 farthest_pptr = std::max(farthest_pptr, pptr());
238 off_type n_written = (
off_type)(farthest_pptr - pbase());
239 bp::str chunk(pbase(), farthest_pptr);
241 if (!traits_type::eq_int_type(c, traits_type::eof())) {
242 py_write(traits_type::to_char_type(c));
246 pos_of_write_buffer_end_in_py_file += n_written;
247 setp(pbase(), epptr());
249 farthest_pptr = pptr();
251 return traits_type::eq_int_type(c, traits_type::eof())
252 ? traits_type::not_eof(c)
265 farthest_pptr = std::max(farthest_pptr, pptr());
266 if (farthest_pptr && farthest_pptr > pbase()) {
267 off_type delta = pptr() - farthest_pptr;
269 if (traits_type::eq_int_type(status, traits_type::eof())) result = -1;
270 if (py_seek != bp::object()) py_seek(delta, 1);
271 }
else if (gptr() && gptr() < egptr()) {
272 if (py_seek != bp::object()) py_seek(gptr() - egptr(), 1);
284 virtual pos_type
seekoff(off_type off, std::ios_base::seekdir way,
285 std::ios_base::openmode which = std::ios_base::in |
286 std::ios_base::out) {
294 if (py_seek == bp::object()) {
295 throw std::invalid_argument(
296 "That Python file object has no 'seek' attribute");
300 if (which == std::ios_base::in && !gptr()) {
301 if (traits_type::eq_int_type(
underflow(), traits_type::eof())) {
309 case std::ios_base::beg:
312 case std::ios_base::cur:
315 case std::ios_base::end:
323 boost::optional<off_type> result =
324 seekoff_without_calling_python(off, way, which);
327 if (which == std::ios_base::out)
overflow();
328 if (way == std::ios_base::cur) {
329 if (which == std::ios_base::in)
330 off -= egptr() - gptr();
331 else if (which == std::ios_base::out)
332 off += pptr() - pbase();
334 py_seek(off, whence);
335 result =
off_type(bp::extract<off_type>(py_tell()));
336 if (which == std::ios_base::in)
underflow();
343 std::ios_base::openmode which = std::ios_base::in |
344 std::ios_base::out) {
349 bp::object py_read, py_write, py_seek, py_tell;
351 std::size_t buffer_size;
358 bp::object read_buffer;
365 off_type pos_of_read_buffer_end_in_py_file,
366 pos_of_write_buffer_end_in_py_file;
371 boost::optional<off_type> seekoff_without_calling_python(
372 off_type off, std::ios_base::seekdir way, std::ios_base::openmode which) {
373 boost::optional<off_type>
const failure;
376 off_type buf_begin, buf_end, buf_cur, upper_bound;
377 off_type pos_of_buffer_end_in_py_file;
378 if (which == std::ios_base::in) {
379 pos_of_buffer_end_in_py_file = pos_of_read_buffer_end_in_py_file;
380 buf_begin =
reinterpret_cast<std::streamsize
>(eback());
381 buf_cur =
reinterpret_cast<std::streamsize
>(gptr());
382 buf_end =
reinterpret_cast<std::streamsize
>(egptr());
383 upper_bound = buf_end;
384 }
else if (which == std::ios_base::out) {
385 pos_of_buffer_end_in_py_file = pos_of_write_buffer_end_in_py_file;
386 buf_begin =
reinterpret_cast<std::streamsize
>(pbase());
387 buf_cur =
reinterpret_cast<std::streamsize
>(pptr());
388 buf_end =
reinterpret_cast<std::streamsize
>(epptr());
389 farthest_pptr = std::max(farthest_pptr, pptr());
390 upper_bound =
reinterpret_cast<std::streamsize
>(farthest_pptr) + 1;
397 if (way == std::ios_base::cur) {
398 buf_sought = buf_cur + off;
399 }
else if (way == std::ios_base::beg) {
400 buf_sought = buf_end + (off - pos_of_buffer_end_in_py_file);
401 }
else if (way == std::ios_base::end) {
408 if (buf_sought < buf_begin || buf_sought >= upper_bound)
return failure;
411 if (which == std::ios_base::in)
412 gbump(buf_sought - buf_cur);
413 else if (which == std::ios_base::out)
414 pbump(buf_sought - buf_cur);
415 return pos_of_buffer_end_in_py_file + (buf_sought - buf_end);
422 exceptions(std::ios_base::badbit);
438 exceptions(std::ios_base::badbit);
442 if (this->good()) this->flush();
453 : python_streambuf(python_file_obj, buffer_size) {}
457 ostream(bp::object& python_file_obj, std::size_t buffer_size = 0)
463 if (this->good()) this->flush();
464 }
catch (bp::error_already_set&) {
466 throw std::runtime_error(
467 "Problem closing python ostream.\n" 468 " Known limitation: the error is unrecoverable. Sorry.\n" 469 " Suggestion for programmer: add ostream.flush() before"
streambuf_capsule(bp::object &python_file_obj, std::size_t buffer_size=0)
virtual int sync()
Update the python file to reflect the state of this stream buffer.
ostream(bp::object &python_file_obj, std::size_t buffer_size=0)
static const std::size_t default_buffer_size
The default size of the read and write buffer.
streambuf python_streambuf
#define CHECK_INVARIANT(expr, mess)
streambuf(bp::object &python_file_obj, std::size_t buffer_size_=0)
Construct from a Python file object.
virtual int_type underflow()
C.f. C++ standard section 27.5.2.4.3.
virtual int_type overflow(int_type c=traits_type_eof())
C.f. C++ standard section 27.5.2.4.5.
base_t::off_type off_type
base_t::pos_type pos_type
static int traits_type_eof()
#define TEST_ASSERT(expr)
base_t::int_type int_type
virtual pos_type seekoff(off_type off, std::ios_base::seekdir way, std::ios_base::openmode which=std::ios_base::in|std::ios_base::out)
C.f. C++ standard section 27.5.2.4.2.
virtual pos_type seekpos(pos_type sp, std::ios_base::openmode which=std::ios_base::in|std::ios_base::out)
C.f. C++ standard section 27.5.2.4.2.
virtual std::streamsize showmanyc()
C.f. C++ standard section 27.5.2.4.3.
virtual ~streambuf()
Mundane destructor freeing the allocated resources.
base_t::char_type char_type
A stream buffer getting data from and putting data into a Python file object.
base_t::traits_type traits_type