graph_python_interface.hh 18.7 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
#include <functional>

namespace std
{
    template<>
    struct hash<boost::python::object>
    {
        size_t operator()(const boost::python::object& o) const
        {
33
            return std::hash<int64_t>()(boost::python::extract<int64_t>(o.attr("__hash__")()));
Tiago Peixoto's avatar
Tiago Peixoto committed
34
35
36
37
        }
    };
}

38
39
#include <boost/graph/graph_traits.hpp>
#include <boost/mpl/logical.hpp>
40
#include <boost/iterator/iterator_facade.hpp>
41

Tiago Peixoto's avatar
Tiago Peixoto committed
42
43
#include <type_traits>

44
45
#include "graph.hh"
#include "graph_filtering.hh"
46
#include "graph_selectors.hh"
47
#include "numpy_bind.hh"
48

49

50
// this file includes a simple python interface for the internally kept
51
52
53
54
// 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).
55

56
57
58
namespace graph_tool
{

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


83
// forward declaration of PythonEdge
84
85
template <class Graph>
class PythonEdge;
86

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

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

105
    void SetValid(bool valid)
106
    {
107
108
        _valid = valid;
    }
109

110
111
112
    void CheckValid() const
    {
        if (!IsValid())
113
            throw ValueException("invalid vertex descriptor: " +
Tiago Peixoto's avatar
Tiago Peixoto committed
114
                                 boost::lexical_cast<string>(_v));
115
    }
116

Tiago Peixoto's avatar
Tiago Peixoto committed
117
    boost::python::object GetGraph() const
118
119
120
121
    {
        return _g();
    }

122
    GraphInterface::vertex_t GetDescriptor() const
123
124
125
126
    {
        return _v;
    }

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

        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));
        }
146
147
    };

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

159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
    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;
    }

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

185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200

    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;
    }

201
    // provide iterator support for out_edges
202

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

Tiago Peixoto's avatar
Tiago Peixoto committed
218
    boost::python::object OutEdges() const
Tiago Peixoto's avatar
Tiago Peixoto committed
219
    {
220
        CheckValid();
Tiago Peixoto's avatar
Tiago Peixoto committed
221
222
223
224
        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)))();
225
        return iter;
226
    }
227

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

Tiago Peixoto's avatar
Tiago Peixoto committed
243
    boost::python::object InEdges() const
244
    {
245
        CheckValid();
Tiago Peixoto's avatar
Tiago Peixoto committed
246
247
248
249
250
        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)))();
251
        return iter;
Tiago Peixoto's avatar
Tiago Peixoto committed
252
    }
253

254
255
    std::string GetString() const
    {
256
        CheckValid();
Tiago Peixoto's avatar
Tiago Peixoto committed
257
        return boost::lexical_cast<std::string>(_v);
258
259
    }

260
    size_t GetHash() const
261
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
262
        return std::hash<size_t>()(_v);
263
264
    }

265
266
    size_t GetIndex() const
    {
267
        return _v;
268
269
    }

270
private:
Tiago Peixoto's avatar
Tiago Peixoto committed
271
    boost::python::object _g;
272
    GraphInterface::vertex_t _v;
273
    bool _valid;
274
};
275

276
// below are classes related to the PythonEdge type
277

278
279
class EdgeBase {}; // useful to unite all edge types

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

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

298
299
300
301
302
303
304
305
306
307
        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());

308
        return valid;
309
310
311
312
313
314
315
316
317
    }

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

    void CheckValid() const
    {
318
        if (!IsValid())
Tiago Peixoto's avatar
Tiago Peixoto committed
319
            throw ValueException("invalid edge descriptor");
Tiago Peixoto's avatar
Tiago Peixoto committed
320
321
    }

Tiago Peixoto's avatar
Tiago Peixoto committed
322
    boost::python::object GetGraph() const
323
324
325
326
    {
        return _g();
    }

327
    GraphInterface::edge_t GetDescriptor() const
328
329
330
331
    {
        return _e;
    }

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

Tiago Peixoto's avatar
Tiago Peixoto committed
344
    boost::python::object GetSource() const
Tiago Peixoto's avatar
Tiago Peixoto committed
345
    {
346
        CheckValid();
Tiago Peixoto's avatar
Tiago Peixoto committed
347
348
349
350
351
        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)))();
352
        return v;
Tiago Peixoto's avatar
Tiago Peixoto committed
353
354
    }

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

Tiago Peixoto's avatar
Tiago Peixoto committed
367
    boost::python::object GetTarget() const
Tiago Peixoto's avatar
Tiago Peixoto committed
368
    {
369
        CheckValid();
Tiago Peixoto's avatar
Tiago Peixoto committed
370
371
372
373
        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)))();
374
        return v;
Tiago Peixoto's avatar
Tiago Peixoto committed
375
376
    }

377
378
    std::string GetString() const
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
379
380
        PythonVertex src = boost::python::extract<PythonVertex>(GetSource());
        PythonVertex tgt = boost::python::extract<PythonVertex>(GetTarget());
381
        return "(" + src.GetString() + ", " + tgt.GetString() + ")";
382
383
    }

384
    size_t GetHash() const
Tiago Peixoto's avatar
Tiago Peixoto committed
385
    {
386
        CheckValid();
Tiago Peixoto's avatar
Tiago Peixoto committed
387
388
        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
389
390
    }

391
private:
Tiago Peixoto's avatar
Tiago Peixoto committed
392
    boost::python::object _g;
393
    edge_descriptor _e;
394
    bool _valid;
395
396
};

397
398
399
400
401
402
403
404
405
406
// 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
407
408
409
410
411
412
413
414
        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
415
                >::type,
Tiago Peixoto's avatar
Tiago Peixoto committed
416
417
            boost::mpl::bool_<true>,
            boost::mpl::bool_<false> >::type type;
418
419
420
421
    };
};

template <class PropertyMap>
422
423
424
class PythonPropertyMap
{
public:
425
426
    PythonPropertyMap(const PropertyMap& pmap)
        : _pmap(pmap) {}
427

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

Tiago Peixoto's avatar
Tiago Peixoto committed
430
    typedef typename boost::mpl::if_<
431
432
433
434
        typename return_reference::apply<value_type>::type,
        value_type&,
        value_type>::type reference;

435
    template <class PythonDescriptor>
436
    reference GetValue(const PythonDescriptor& key)
437
    {
438
        key.CheckValid();
439
        return get(_pmap, key.GetDescriptor());
440
441
    }

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

452
453
    template <class PythonDescriptor>
    void set_value(const PythonDescriptor& key, const value_type& val,
Tiago Peixoto's avatar
Tiago Peixoto committed
454
                   std::true_type)
455
    {
456
457
        key.CheckValid();
        put(_pmap, key.GetDescriptor(), val);
458
459
    }

460
    template <class PythonDescriptor>
461
    void set_value(const PythonDescriptor&, const value_type&,
Tiago Peixoto's avatar
Tiago Peixoto committed
462
                   std::false_type)
463
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
464
        throw ValueException("property is read-only");
465
466
467
468
    }

    size_t GetHash() const
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
469
        return std::hash<size_t>()(size_t(this));
470
471
472
473
    }

    std::string GetType() const
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
474
475
476
        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)
477
478
            return gcc_demangle(typeid(value_type).name());
        else
Tiago Peixoto's avatar
Tiago Peixoto committed
479
            return type_names[boost::mpl::find<value_types,
480
                                        value_type>::type::pos::value];
481
482
    }

483
    boost::any GetMap() const
484
485
486
487
    {
        return _pmap;
    }

488
489
    boost::any GetDynamicMap() const
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
490
        return (boost::dynamic_property_map*)
491
492
493
494
            (new boost::detail::dynamic_property_map_adaptor<PropertyMap>
             (_pmap));
    }

Tiago Peixoto's avatar
Tiago Peixoto committed
495
    boost::python::object GetArray(size_t size)
496
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
497
498
499
500
501
502
503
504
        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 >
505
            ::type>::type isnt_vector_map;
506
        return get_array(size, isnt_vector_map());
507
508
    }

509
    boost::python::object get_array(size_t size, boost::mpl::bool_<false>)
510
511
512
513
514
    {
        _pmap.reserve(size);
        return wrap_vector_not_owned(_pmap.get_storage());
    }

515
    boost::python::object get_array(size_t, boost::mpl::bool_<true>)
516
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
517
        return boost::python::object();
518
519
    }

520
521
    bool IsWritable() const
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
522
523
        return std::is_convertible<typename boost::property_traits<PropertyMap>::category,
                                   boost::writable_property_map_tag>::value;
524
525
    }

526
private:
527
    PropertyMap _pmap; // hold an internal copy, since it's cheap
528
529
530
};


531
532
533
534
535
536
537
538
//
// 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
539
                     boost::any pmap, boost::python::object& new_prop, bool& found) const
540
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
541
        size_t i = boost::mpl::find<value_types,ValueType>::type::pos::value;
542
543
        if (type_name == type_names[i])
        {
544
545
            typedef typename property_map_type::apply<ValueType, IndexMap>::type
                map_t;
546
547
548
549
            map_t prop;
            if (pmap.empty())
                prop = map_t(index);
            else
Tiago Peixoto's avatar
Tiago Peixoto committed
550
                prop = boost::any_cast<map_t>(pmap);
551

Tiago Peixoto's avatar
Tiago Peixoto committed
552
            new_prop = boost::python::object(PythonPropertyMap<map_t>(prop));
553
554
555
556
557
558
            found = true;
        }
    }
};

template <class IndexMap>
Tiago Peixoto's avatar
Tiago Peixoto committed
559
560
boost::python::object new_property(const string& type, IndexMap index_map,
                                   boost::any pmap)
561
{
Tiago Peixoto's avatar
Tiago Peixoto committed
562
    boost::python::object prop;
563
    bool found = false;
Tiago Peixoto's avatar
Tiago Peixoto committed
564
565
566
567
    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)));
568
    if (!found)
Tiago Peixoto's avatar
Tiago Peixoto committed
569
        throw ValueException("Invalid property type: " + type);
570
571
572
    return prop;
}

573
574
575
576
577
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
//
// 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;
};


624
625
626
} //graph_tool namespace

#endif