graph_python_interface.hh 18.8 KB
Newer Older
1 2
// graph-tool -- a general graph modification and manipulation thingy
//
Tiago Peixoto's avatar
Tiago Peixoto committed
3
// Copyright (C) 2006-2015 Tiago de Paula Peixoto <tiago@skewed.de>
4 5 6
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
Tiago Peixoto's avatar
Tiago Peixoto committed
7
// as published by the Free Software Foundation; either version 3
8 9 10 11 12 13 14 15
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
16
// along with this program. If not, see <http://www.gnu.org/licenses/>.
17

18 19
#ifndef PYTHON_INTERFACE_HH
#define PYTHON_INTERFACE_HH
20

21 22 23
#include <boost/python.hpp>
#include <boost/python/type_id.hpp>

Tiago Peixoto's avatar
Tiago Peixoto committed
24 25 26 27 28 29 30 31 32 33 34 35 36 37
#include <functional>

namespace std
{
    template<>
    struct hash<boost::python::object>
    {
        size_t operator()(const boost::python::object& o) const
        {
            return boost::python::extract<size_t>(o.attr("__hash__")());
        }
    };
}

38 39 40 41 42
namespace boost
{
    size_t hash_value(const boost::python::object& o);
}

43 44
#include <boost/graph/graph_traits.hpp>
#include <boost/mpl/logical.hpp>
45
#include <boost/iterator/iterator_facade.hpp>
46

Tiago Peixoto's avatar
Tiago Peixoto committed
47 48
#include <type_traits>

49 50
#include "graph.hh"
#include "graph_filtering.hh"
51
#include "graph_selectors.hh"
52
#include "numpy_bind.hh"
53

54

55
// this file includes a simple python interface for the internally kept
56 57 58 59
// graph. It defines a PythonVertex, PythonEdge and PythonIterator template
// classes, which contain the proper member functions for graph traversal. These
// types are then specialized for each version of the adapted graph (directed,
// undirected, filtered, reversed).
60

61 62 63
namespace graph_tool
{

64 65
// generic iterator adaptor which can be used to iterate vertices, edges,
// out_edges and in_edges through python
66
template <class Descriptor, class Iterator>
67
class PythonIterator
68
{
69
public:
70 71 72
    PythonIterator(const boost::python::object& g,
                   std::pair<Iterator,Iterator> e)
        : _wg(g), _g(g()), _e(e) {}
73
    Descriptor Next()
74
    {
75
        if (_e.first == _e.second)
Tiago Peixoto's avatar
Tiago Peixoto committed
76
            boost::python::objects::stop_iteration_error();
77
        Descriptor e(_wg, *_e.first);
78 79
        ++_e.first;
        return e;
80 81
    }
private:
82
    boost::python::object _wg;
Tiago Peixoto's avatar
Tiago Peixoto committed
83
    boost::python::object _g;
84
    std::pair<Iterator,Iterator> _e;
Tiago Peixoto's avatar
Tiago Peixoto committed
85 86 87
};


88
// forward declaration of PythonEdge
89 90
template <class Graph>
class PythonEdge;
91

92
// below are classes related to the PythonVertex type
93
class PythonVertex
94 95
{
public:
Tiago Peixoto's avatar
Tiago Peixoto committed
96
    PythonVertex(const boost::python::object& g, GraphInterface::vertex_t v):
97
        _g(g), _v(v), _valid(true)
98
    {}
99 100 101

    bool IsValid() const
    {
102 103
        if (_g().ptr() == Py_None)
            return false;
Tiago Peixoto's avatar
Tiago Peixoto committed
104
        GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
105
        return _valid &&
Tiago Peixoto's avatar
Tiago Peixoto committed
106
            (_v != boost::graph_traits<GraphInterface::multigraph_t>::null_vertex()) &&
107
            (_v < num_vertices(*gi._mg));
108
    }
109

110
    void SetValid(bool valid)
111
    {
112 113
        _valid = valid;
    }
114

115 116 117
    void CheckValid() const
    {
        if (!IsValid())
118
            throw ValueException("invalid vertex descriptor: " +
Tiago Peixoto's avatar
Tiago Peixoto committed
119
                                 boost::lexical_cast<string>(_v));
120
    }
121

Tiago Peixoto's avatar
Tiago Peixoto committed
122
    boost::python::object GetGraph() const
123 124 125 126
    {
        return _g();
    }

127
    GraphInterface::vertex_t GetDescriptor() const
128 129 130 131
    {
        return _v;
    }

132 133 134 135
    template <class DegSelector>
    struct get_degree
    {
        template<class Graph>
136
        void operator()(const Graph& g,
Tiago Peixoto's avatar
Tiago Peixoto committed
137
                        typename boost::graph_traits<Graph>::vertex_descriptor v,
138 139
                        size_t& deg) const
        {
140
            deg = DegSelector()(v, g);
141
        }
142 143 144 145 146 147 148 149 150

        template<class Graph, class PMap>
        void operator()(const Graph& g,
                        typename boost::graph_traits<Graph>::vertex_descriptor v,
                        const PMap& weight,
                        boost::python::object& deg) const
        {
            deg = boost::python::object(DegSelector()(v, g, weight));
        }
151 152
    };

153
    size_t GetInDegree() const
154
    {
155
        CheckValid();
Tiago Peixoto's avatar
Tiago Peixoto committed
156
        GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
157
        size_t in_deg;
Tiago Peixoto's avatar
Tiago Peixoto committed
158 159 160
        run_action<>()(gi, std::bind(get_degree<in_degreeS>(),
                                     std::placeholders::_1, _v,
                                     std::ref(in_deg)))();
161
        return in_deg;
162
    }
163

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
    boost::python::object GetWeightedInDegree(boost::any pmap) const
    {
        if (!belongs<edge_scalar_properties>()(pmap))
            throw ValueException("edge weight property must be of scalar type");
        CheckValid();
        GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
        boost::python::object in_deg;
        run_action<>()(gi, std::bind(get_degree<in_degreeS>(),
                                     std::placeholders::_1, _v,
                                     std::placeholders::_2,
                                     std::ref(in_deg)),
                       edge_scalar_properties())(pmap);
        return in_deg;
    }

179
    size_t GetOutDegree() const
180
    {
181
        CheckValid();
Tiago Peixoto's avatar
Tiago Peixoto committed
182
        GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
183
        size_t out_deg;
Tiago Peixoto's avatar
Tiago Peixoto committed
184 185 186
        run_action<>()(gi, std::bind(get_degree<out_degreeS>(),
                                     std::placeholders::_1, _v,
                                     std::ref(out_deg)))();
187
        return out_deg;
Tiago Peixoto's avatar
Tiago Peixoto committed
188 189
    }

190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205

    boost::python::object GetWeightedOutDegree(boost::any pmap) const
    {
        if (!belongs<edge_scalar_properties>()(pmap))
            throw ValueException("edge weight property must be of scalar type");
        CheckValid();
        GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
        boost::python::object out_deg;
        run_action<>()(gi, std::bind(get_degree<out_degreeS>(),
                                     std::placeholders::_1, _v,
                                     std::placeholders::_2,
                                     std::ref(out_deg)),
                       edge_scalar_properties())(pmap);
        return out_deg;
    }

206
    // provide iterator support for out_edges
207

208 209 210
    struct get_out_edges
    {
        template<class Graph>
Tiago Peixoto's avatar
Tiago Peixoto committed
211 212 213
        void operator()(const Graph& g, const boost::python::object& pg,
                        typename boost::graph_traits<Graph>::vertex_descriptor v,
                        boost::python::object& iter) const
214
        {
Tiago Peixoto's avatar
Tiago Peixoto committed
215
            typedef typename boost::graph_traits<Graph>::out_edge_iterator
216
                out_edge_iterator;
Tiago Peixoto's avatar
Tiago Peixoto committed
217
            iter = boost::python::object(PythonIterator<PythonEdge<Graph>,
218
                                                 out_edge_iterator>
219
                                  (pg, out_edges(v, g)));
220 221 222
        }
    };

Tiago Peixoto's avatar
Tiago Peixoto committed
223
    boost::python::object OutEdges() const
Tiago Peixoto's avatar
Tiago Peixoto committed
224
    {
225
        CheckValid();
Tiago Peixoto's avatar
Tiago Peixoto committed
226 227 228 229
        GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
        boost::python::object iter;
        run_action<>()(gi, std::bind(get_out_edges(), std::placeholders::_1,
                                     std::ref(_g), _v, std::ref(iter)))();
230
        return iter;
231
    }
232

233 234 235
    struct get_in_edges
    {
        template<class Graph>
Tiago Peixoto's avatar
Tiago Peixoto committed
236 237 238
        void operator()(const Graph& g, const boost::python::object& pg,
                        typename boost::graph_traits<Graph>::vertex_descriptor v,
                        boost::python::object& iter) const
239 240 241
        {
            typedef typename in_edge_iteratorS<Graph>::type
                in_edge_iterator;
Tiago Peixoto's avatar
Tiago Peixoto committed
242
            iter = boost::python::object
243
                (PythonIterator<PythonEdge<Graph>,in_edge_iterator>
244
                 (pg, in_edge_iteratorS<Graph>::get_edges(v, g)));
245 246 247
        }
    };

Tiago Peixoto's avatar
Tiago Peixoto committed
248
    boost::python::object InEdges() const
249
    {
250
        CheckValid();
Tiago Peixoto's avatar
Tiago Peixoto committed
251 252 253 254 255
        GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
        boost::python::object iter;
        run_action<>()(gi, std::bind(get_in_edges(), placeholders::_1,
                                     std::ref(_g), _v,
                                     std::ref(iter)))();
256
        return iter;
Tiago Peixoto's avatar
Tiago Peixoto committed
257
    }
258

259 260
    std::string GetString() const
    {
261
        CheckValid();
Tiago Peixoto's avatar
Tiago Peixoto committed
262
        return boost::lexical_cast<std::string>(_v);
263 264
    }

265
    size_t GetHash() const
266
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
267
        return std::hash<size_t>()(_v);
268 269
    }

270 271
    size_t GetIndex() const
    {
272
        return _v;
273 274
    }

275
private:
Tiago Peixoto's avatar
Tiago Peixoto committed
276
    boost::python::object _g;
277
    GraphInterface::vertex_t _v;
278
    bool _valid;
279
};
280

281
// below are classes related to the PythonEdge type
282

283 284
class EdgeBase {}; // useful to unite all edge types

285
template <class Graph>
286
class PythonEdge : public EdgeBase
287 288
{
public:
Tiago Peixoto's avatar
Tiago Peixoto committed
289 290
    typedef typename boost::graph_traits<Graph>::edge_descriptor edge_descriptor;
    PythonEdge(const boost::python::object& g, edge_descriptor e)
291
        : _g(g), _e(e), _valid(true)
292 293 294 295
    {
    }

    bool IsValid() const
Tiago Peixoto's avatar
Tiago Peixoto committed
296
    {
297 298
        boost::python::object g = _g();
        if (g.ptr() == Py_None || !_valid)
299
            return false;
300
        GraphInterface& gi = boost::python::extract<GraphInterface&>(g.attr("_Graph__graph"));
301
        GraphInterface::edge_t e(_e);
302

303 304 305 306 307 308 309 310 311 312
        typename GraphInterface::multigraph_t::vertex_t s, t;
        s = source(e, *gi._mg);
        t = target(e, *gi._mg);

        bool valid = ((s != boost::graph_traits<GraphInterface::multigraph_t>::null_vertex()) &&
                      (s < num_vertices(*gi._mg)) &&
                      (t != boost::graph_traits<GraphInterface::multigraph_t>::null_vertex()) &&
                      (t < num_vertices(*gi._mg)) &&
                      gi.GetEdgeIndex()[e] <= gi.GetMaxEdgeIndex());

313
        return valid;
314 315 316 317 318 319 320 321 322
    }

    void SetValid(bool valid)
    {
        _valid = valid;
    }

    void CheckValid() const
    {
323
        if (!IsValid())
Tiago Peixoto's avatar
Tiago Peixoto committed
324
            throw ValueException("invalid edge descriptor");
Tiago Peixoto's avatar
Tiago Peixoto committed
325 326
    }

Tiago Peixoto's avatar
Tiago Peixoto committed
327
    boost::python::object GetGraph() const
328 329 330 331
    {
        return _g();
    }

332
    GraphInterface::edge_t GetDescriptor() const
333 334 335 336
    {
        return _e;
    }

337 338 339
    struct get_source
    {
        template<class GraphType>
Tiago Peixoto's avatar
Tiago Peixoto committed
340 341
        void operator()(const GraphType& g, const boost::python::object& pg,
                        const edge_descriptor& edge, boost::python::object& vertex)
342 343
            const
        {
Tiago Peixoto's avatar
Tiago Peixoto committed
344 345
            typedef typename boost::graph_traits<GraphType>::edge_descriptor edge_t;
            vertex = boost::python::object(PythonVertex(pg, source(edge_t(edge), g)));
346 347
        }
    };
348

Tiago Peixoto's avatar
Tiago Peixoto committed
349
    boost::python::object GetSource() const
Tiago Peixoto's avatar
Tiago Peixoto committed
350
    {
351
        CheckValid();
Tiago Peixoto's avatar
Tiago Peixoto committed
352 353 354 355 356
        GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
        boost::python::object v;
        run_action<>()(gi, std::bind(get_source(), std::placeholders::_1,
                                     std::ref(_g), std::ref(_e),
                                     std::ref(v)))();
357
        return v;
Tiago Peixoto's avatar
Tiago Peixoto committed
358 359
    }

360 361 362
    struct get_target
    {
        template<class GraphType>
Tiago Peixoto's avatar
Tiago Peixoto committed
363 364
        void operator()(const GraphType& g, const boost::python::object& pg,
                        const edge_descriptor& edge, boost::python::object& vertex)
365
            const
366
        {
Tiago Peixoto's avatar
Tiago Peixoto committed
367 368
            typedef typename boost::graph_traits<GraphType>::edge_descriptor edge_t;
            vertex = boost::python::object(PythonVertex(pg, target(edge_t(edge), g)));
369 370
        }
    };
371

Tiago Peixoto's avatar
Tiago Peixoto committed
372
    boost::python::object GetTarget() const
Tiago Peixoto's avatar
Tiago Peixoto committed
373
    {
374
        CheckValid();
Tiago Peixoto's avatar
Tiago Peixoto committed
375 376 377 378
        GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
        boost::python::object v;
        run_action<>()(gi, std::bind(get_target(), std::placeholders::_1,
                                     std::ref(_g), std::ref(_e), std::ref(v)))();
379
        return v;
Tiago Peixoto's avatar
Tiago Peixoto committed
380 381
    }

382 383
    std::string GetString() const
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
384 385
        PythonVertex src = boost::python::extract<PythonVertex>(GetSource());
        PythonVertex tgt = boost::python::extract<PythonVertex>(GetTarget());
386
        return "(" + src.GetString() + ", " + tgt.GetString() + ")";
387 388
    }

389
    size_t GetHash() const
Tiago Peixoto's avatar
Tiago Peixoto committed
390
    {
391
        CheckValid();
Tiago Peixoto's avatar
Tiago Peixoto committed
392 393
        GraphInterface& gi = boost::python::extract<GraphInterface&>(_g().attr("_Graph__graph"));
        return std::hash<size_t>()(gi._edge_index[_e]);
Tiago Peixoto's avatar
Tiago Peixoto committed
394 395
    }

396
private:
Tiago Peixoto's avatar
Tiago Peixoto committed
397
    boost::python::object _g;
398
    edge_descriptor _e;
399
    bool _valid;
400 401
};

402 403 404 405 406 407 408 409 410 411
// metafunction to determine wether or not to return copies or internal
// references to property types
struct return_reference
{
    template <class ValueType>
    struct apply
    {
        // return actual references only for non-string and non-python::object
        // classes

Tiago Peixoto's avatar
Tiago Peixoto committed
412 413 414 415 416 417 418 419
        typedef typename boost::mpl::if_<
            typename boost::mpl::and_<
                std::is_class<ValueType>,
                typename boost::mpl::and_<
                    typename boost::mpl::not_<std::is_same<ValueType,
                                                           string> >::type,
                    typename boost::mpl::not_<std::is_same<ValueType,
                                                           boost::python::object> >::type>::type
420
                >::type,
Tiago Peixoto's avatar
Tiago Peixoto committed
421 422
            boost::mpl::bool_<true>,
            boost::mpl::bool_<false> >::type type;
423 424 425 426
    };
};

template <class PropertyMap>
427 428 429
class PythonPropertyMap
{
public:
430 431
    PythonPropertyMap(const PropertyMap& pmap)
        : _pmap(pmap) {}
432

Tiago Peixoto's avatar
Tiago Peixoto committed
433
    typedef typename boost::property_traits<PropertyMap>::value_type value_type;
434

Tiago Peixoto's avatar
Tiago Peixoto committed
435
    typedef typename boost::mpl::if_<
436 437 438 439
        typename return_reference::apply<value_type>::type,
        value_type&,
        value_type>::type reference;

440
    template <class PythonDescriptor>
441
    reference GetValue(const PythonDescriptor& key)
442
    {
443
        key.CheckValid();
444
        return get(_pmap, key.GetDescriptor());
445 446
    }

447 448
    // in this case, val should be a copy, not a reference. This is to avoid a
    // problem with vector-valued property maps
449
    template <class PythonDescriptor>
450
    void SetValue(const PythonDescriptor& key, value_type val)
451
    {
452
        set_value(key, val,
Tiago Peixoto's avatar
Tiago Peixoto committed
453 454
                  std::is_convertible<typename boost::property_traits<PropertyMap>::category,
                                      boost::writable_property_map_tag>());
455 456
    }

457 458
    template <class PythonDescriptor>
    void set_value(const PythonDescriptor& key, const value_type& val,
Tiago Peixoto's avatar
Tiago Peixoto committed
459
                   std::true_type)
460
    {
461 462
        key.CheckValid();
        put(_pmap, key.GetDescriptor(), val);
463 464
    }

465
    template <class PythonDescriptor>
466
    void set_value(const PythonDescriptor&, const value_type&,
Tiago Peixoto's avatar
Tiago Peixoto committed
467
                   std::false_type)
468
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
469
        throw ValueException("property is read-only");
470 471 472 473
    }

    size_t GetHash() const
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
474
        return std::hash<size_t>()(size_t(this));
475 476 477 478
    }

    std::string GetType() const
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
479 480 481
        using boost::python::detail::gcc_demangle;
        if (std::is_same<typename boost::mpl::find<value_types,value_type>::type,
                         typename boost::mpl::end<value_types>::type>::value)
482 483
            return gcc_demangle(typeid(value_type).name());
        else
Tiago Peixoto's avatar
Tiago Peixoto committed
484
            return type_names[boost::mpl::find<value_types,
485
                                        value_type>::type::pos::value];
486 487
    }

488
    boost::any GetMap() const
489 490 491 492
    {
        return _pmap;
    }

493 494
    boost::any GetDynamicMap() const
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
495
        return (boost::dynamic_property_map*)
496 497 498 499
            (new boost::detail::dynamic_property_map_adaptor<PropertyMap>
             (_pmap));
    }

Tiago Peixoto's avatar
Tiago Peixoto committed
500
    boost::python::object GetArray(size_t size)
501
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
502 503 504 505 506 507 508 509
        typedef typename boost::mpl::or_<
            typename boost::mpl::or_<
                std::is_same<PropertyMap,
                             GraphInterface::vertex_index_map_t>,
                std::is_same<PropertyMap,
                             GraphInterface::edge_index_map_t> >::type,
            typename boost::mpl::not_<
                typename boost::mpl::has_key<numpy_types, value_type>::type >
510
            ::type>::type isnt_vector_map;
511
        return get_array(size, isnt_vector_map());
512 513
    }

514
    boost::python::object get_array(size_t size, boost::mpl::bool_<false>)
515 516 517 518 519
    {
        _pmap.reserve(size);
        return wrap_vector_not_owned(_pmap.get_storage());
    }

520
    boost::python::object get_array(size_t, boost::mpl::bool_<true>)
521
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
522
        return boost::python::object();
523 524
    }

525 526
    bool IsWritable() const
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
527 528
        return std::is_convertible<typename boost::property_traits<PropertyMap>::category,
                                   boost::writable_property_map_tag>::value;
529 530
    }

531
private:
532
    PropertyMap _pmap; // hold an internal copy, since it's cheap
533 534 535
};


536 537 538 539 540 541 542 543
//
// Create new properties
//

struct new_property_map
{
    template <class ValueType, class IndexMap>
    void operator()(ValueType, IndexMap index, const string& type_name,
Tiago Peixoto's avatar
Tiago Peixoto committed
544
                     boost::any pmap, boost::python::object& new_prop, bool& found) const
545
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
546
        size_t i = boost::mpl::find<value_types,ValueType>::type::pos::value;
547 548
        if (type_name == type_names[i])
        {
549 550
            typedef typename property_map_type::apply<ValueType, IndexMap>::type
                map_t;
551 552 553 554
            map_t prop;
            if (pmap.empty())
                prop = map_t(index);
            else
Tiago Peixoto's avatar
Tiago Peixoto committed
555
                prop = boost::any_cast<map_t>(pmap);
556

Tiago Peixoto's avatar
Tiago Peixoto committed
557
            new_prop = boost::python::object(PythonPropertyMap<map_t>(prop));
558 559 560 561 562 563
            found = true;
        }
    }
};

template <class IndexMap>
Tiago Peixoto's avatar
Tiago Peixoto committed
564 565
boost::python::object new_property(const string& type, IndexMap index_map,
                                   boost::any pmap)
566
{
Tiago Peixoto's avatar
Tiago Peixoto committed
567
    boost::python::object prop;
568
    bool found = false;
Tiago Peixoto's avatar
Tiago Peixoto committed
569 570 571 572
    boost::mpl::for_each<value_types>(std::bind(new_property_map(),
                                                std::placeholders::_1, index_map,
                                                std::ref(type), pmap, std::ref(prop),
                                                std::ref(found)));
573
    if (!found)
Tiago Peixoto's avatar
Tiago Peixoto committed
574
        throw ValueException("Invalid property type: " + type);
575 576 577
    return prop;
}

578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
//
// Python IO streams (minimal access to c++ streams)
//

class OStream
{
public:
    OStream(std::ostream& s): _s(s) {}

    void Write(const std::string& s, size_t n)
    {
        _s.write(s.c_str(), long(n));
    }

    void Flush()
    {
        _s.flush();
    }

private:
    std::ostream& _s;
};

class IStream
{
public:
    IStream(std::istream& s): _s(s) {}

    boost::python::object Read(size_t n)
    {
        std::string buf;
        buf.resize(n);
        _s.read(&buf[0], n);
        buf.resize(_s.gcount());

#if (PY_MAJOR_VERSION >= 3)
        // in python 3 we need to construct a 'bytes' instance
        PyObject* bytes = PyBytes_FromStringAndSize(&buf[0], buf.size());
        boost::python::handle<> x(bytes);
        boost::python::object pbuf(x);
#else
        boost::python::str pbuf(buf);
#endif
        return pbuf;
    }

private:
    std::istream& _s;
};


629 630 631
} //graph_tool namespace

#endif