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