graph_python_interface.hh 20.2 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-2016 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
51
#ifdef HAVE_BOOST_COROUTINE
#include <boost/coroutine/all.hpp>
#endif // HAVE_BOOST_COROUTINE
52

53
// This file includes a simple python interface for the internally kept
54
55
56
57
// 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).
58

59
60
61
namespace graph_tool
{

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

83
84
85
86
87
88
89
90
91
92
93
94
#ifdef HAVE_BOOST_COROUTINE

// generic coroutine generator adaptor

typedef boost::coroutines::asymmetric_coroutine<boost::python::object> coro_t;

class CoroGenerator
{
public:
    template <class Dispatch>
    CoroGenerator(Dispatch& dispatch)
        : _coro(std::make_shared<coro_t::pull_type>(dispatch)),
95
          _iter(begin(*_coro)), _end(end(*_coro)), _first(true) {}
96
97
    boost::python::object next()
    {
98
99
100
101
        if (_first)
            _first = false;
        else
            ++_iter;
102
103
104
105
106
107
108
109
110
        if (_iter == _end)
            boost::python::objects::stop_iteration_error();
        boost::python::object oe = *_iter;
        return oe;
    }
private:
    std::shared_ptr<coro_t::pull_type> _coro;
    coro_t::pull_type::iterator _iter;
    coro_t::pull_type::iterator _end;
111
    bool _first;
112
113
114
};

#endif // HAVE_BOOST_COROUTINE
Tiago Peixoto's avatar
Tiago Peixoto committed
115

116
// forward declaration of PythonEdge
117
118
template <class Graph>
class PythonEdge;
119

120
121
class VertexBase {}; // useful to unite all vertex

122
// below are classes related to the PythonVertex type
123
124
template <class Graph>
class PythonVertex : public VertexBase
125
126
{
public:
127
    PythonVertex(std::weak_ptr<Graph> g, GraphInterface::vertex_t v):
128
        _g(g), _v(v) {}
129

130
    bool is_valid() const
131
    {
132
        if (_g.expired())
133
            return false;
134
135
136
        std::shared_ptr<Graph> gp = _g.lock();
        Graph& g = *gp.get();
        return _v < num_vertices(g);
137
    }
138

139
    void check_valid() const
140
    {
141
        if (!is_valid())
142
            throw ValueException("invalid vertex descriptor: " +
Tiago Peixoto's avatar
Tiago Peixoto committed
143
                                 boost::lexical_cast<string>(_v));
144
    }
145

146
    GraphInterface::vertex_t get_descriptor() const
147
148
149
150
    {
        return _v;
    }

151
152
153
    template <class DegSelector>
    struct get_degree
    {
154
        void operator()(const Graph& g,
Tiago Peixoto's avatar
Tiago Peixoto committed
155
                        typename boost::graph_traits<Graph>::vertex_descriptor v,
156
157
                        size_t& deg) const
        {
158
            deg = DegSelector()(v, g);
159
        }
160

161
        template<class PMap>
162
163
        void operator()(const Graph& g,
                        typename boost::graph_traits<Graph>::vertex_descriptor v,
164
                        const PMap& weight, boost::python::object& deg) const
165
        {
166
            deg = boost::python::object(DegSelector()(v, g, weight));
167
        }
168
169
    };

170
    size_t get_in_degree() const
171
    {
172
        check_valid();
173
        std::shared_ptr<Graph> gp = _g.lock();
174
        Graph& g = *gp.get();
175
        size_t in_deg;
176
        get_degree<in_degreeS>()(g, _v, in_deg);
177
        return in_deg;
178
    }
179

180
    boost::python::object get_weighted_in_degree(boost::any pmap) const
181
    {
182
183
        check_valid();
        std::shared_ptr<Graph> gp = _g.lock();
184
        Graph& g = *gp.get();
185
        boost::python::object in_deg;
186
        if (!belongs<edge_scalar_properties>()(pmap))
187
            throw ValueException("edge weight property must be of scalar type");
188
189
190
191
192
        gt_dispatch<>()(std::bind(get_degree<in_degreeS>(),
                                  std::ref(g), _v,
                                  std::placeholders::_1,
                                  std::ref(in_deg)),
                        edge_scalar_properties())(pmap);
193
194
195
        return in_deg;
    }

196
    size_t get_out_degree() const
197
    {
198
        check_valid();
199
        std::shared_ptr<Graph> gp = _g.lock();
200
        Graph& g = *gp.get();
201
        size_t out_deg;
202
        get_degree<out_degreeS>()(g, _v, out_deg);
203
        return out_deg;
Tiago Peixoto's avatar
Tiago Peixoto committed
204
205
    }

206

207
    boost::python::object get_weighted_out_degree(boost::any pmap) const
208
    {
209
210
        check_valid();
        std::shared_ptr<Graph> gp = _g.lock();
211
        Graph& g = *gp.get();
212
        boost::python::object out_deg;
213
        if (!belongs<edge_scalar_properties>()(pmap))
214
            throw ValueException("edge weight property must be of scalar type");
215
216
217
218
219
        gt_dispatch<>()(std::bind(get_degree<out_degreeS>(),
                                  std::ref(g), _v,
                                  std::placeholders::_1,
                                  std::ref(out_deg)),
                        edge_scalar_properties())(pmap);
220
221
222
        return out_deg;
    }

223
    // provide iterator support for out_edges
224
    boost::python::object out_edges() const
Tiago Peixoto's avatar
Tiago Peixoto committed
225
    {
226
        check_valid();
227
228
        std::shared_ptr<Graph> gp = _g.lock();
        Graph& g = *gp.get();
229
230
231
232
        typedef typename boost::graph_traits<Graph>::out_edge_iterator
            out_edge_iterator;
        return boost::python::object(PythonIterator<Graph,PythonEdge<Graph>,
                                                    out_edge_iterator>
233
                                     (_g, boost::out_edges(_v, g)));
234
    }
235

236
    boost::python::object in_edges() const
237
    {
238
        check_valid();
239
240
        std::shared_ptr<Graph> gp = _g.lock();
        Graph& g = *gp.get();
241
242
243
244
        typedef typename in_edge_iteratorS<Graph>::type
            in_edge_iterator;
        return boost::python::object(PythonIterator<Graph, PythonEdge<Graph>,
                                                    in_edge_iterator>
245
                                     (_g, in_edge_iteratorS<Graph>::get_edges(_v, g)));
Tiago Peixoto's avatar
Tiago Peixoto committed
246
    }
247

248
    std::string get_string() const
249
    {
250
        check_valid();
Tiago Peixoto's avatar
Tiago Peixoto committed
251
        return boost::lexical_cast<std::string>(_v);
252
253
    }

254
    size_t get_hash() const
255
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
256
        return std::hash<size_t>()(_v);
257
258
    }

259
    size_t get_index() const
260
    {
261
        return _v;
262
263
    }

264
    size_t get_graph_ptr() const
265
    {
266
267
268
269
        if (_g.expired())
            return 0;
        std::shared_ptr<Graph> gp = _g.lock();
        return size_t(gp.get());
270
271
    }

272
    std::string get_graph_type() const
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
    {
        using boost::python::detail::gcc_demangle;
        return gcc_demangle(typeid(Graph).name());
    }

    template <class OGraph>
    bool operator==(const PythonVertex<OGraph>& other) const { return _v == other._v; }
    template <class OGraph>
    bool operator!=(const PythonVertex<OGraph>& other) const { return _v != other._v; }
    template <class OGraph>
    bool operator<(const PythonVertex<OGraph>& other) const { return _v < other._v; }
    template <class OGraph>
    bool operator<=(const PythonVertex<OGraph>& other) const { return _v <= other._v; }
    template <class OGraph>
    bool operator>(const PythonVertex<OGraph>& other) const { return _v > other._v; }
    template <class OGraph>
    bool operator>=(const PythonVertex<OGraph>& other) const { return _v >= other._v; }

291
private:
292
    std::weak_ptr<Graph> _g;
293
    GraphInterface::vertex_t _v;
294
};
295

296
// below are classes related to the PythonEdge type
297

298
299
class EdgeBase {}; // useful to unite all edge types

300
template <class Graph>
301
class PythonEdge : public EdgeBase
302
303
{
public:
Tiago Peixoto's avatar
Tiago Peixoto committed
304
    typedef typename boost::graph_traits<Graph>::edge_descriptor edge_descriptor;
305
    PythonEdge(std::weak_ptr<Graph> g, edge_descriptor e)
306
        : _g(g), _e(e) {}
307

308
    bool is_valid() const
Tiago Peixoto's avatar
Tiago Peixoto committed
309
    {
310
        if (_g.expired())
311
            return false;
312
313
        std::shared_ptr<Graph> gp = _g.lock();
        Graph& g = *gp.get();
314

315
316
        auto s = source(_e, g);
        auto t = target(_e, g);
317

318
        return ((s < num_vertices(g)) && (t < num_vertices(g)));
319
320
    }

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

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

332
    PythonVertex<Graph> get_source() const
333
    {
334
        check_valid();
335
336
337
        std::shared_ptr<Graph> gp = _g.lock();
        Graph& g = *gp.get();
        return PythonVertex<Graph>(gp, source(_e, g));
338
    }
339

340
    PythonVertex<Graph> get_target() const
Tiago Peixoto's avatar
Tiago Peixoto committed
341
    {
342
        check_valid();
343
344
345
        std::shared_ptr<Graph> gp = _g.lock();
        Graph& g = *gp.get();
        return PythonVertex<Graph>(gp, target(_e, g));
Tiago Peixoto's avatar
Tiago Peixoto committed
346
347
    }

348
    std::string get_string() const
349
    {
350
        check_valid();
351
352
        std::shared_ptr<Graph> gp = _g.lock();
        Graph& g = *gp.get();
353
354
355
356
357
        auto s = source(_e, g);
        auto t = target(_e, g);
        return "(" + boost::lexical_cast<std::string>(s) + ", "
            + boost::lexical_cast<std::string>(t) + ")";
    }
358

359
    size_t get_hash() const
Tiago Peixoto's avatar
Tiago Peixoto committed
360
    {
361
        check_valid();
362
363
        std::shared_ptr<Graph> gp = _g.lock();
        Graph& g = *gp.get();
364
365
        auto eindex = get(boost::edge_index_t(), g);
        return std::hash<size_t>()(eindex[_e]);
Tiago Peixoto's avatar
Tiago Peixoto committed
366
367
    }

368
    size_t get_graph_ptr() const
369
    {
370
371
372
373
        if (_g.expired())
            return 0;
        std::shared_ptr<Graph> gp = _g.lock();
        return size_t(gp.get());
374
375
    }

376
    std::string get_graph_type() const
377
378
379
380
381
382
383
384
385
386
387
    {
        using boost::python::detail::gcc_demangle;
        return gcc_demangle(typeid(Graph).name());
    }

    template <class OGraph>
    bool operator==(const PythonEdge<OGraph>& other) const { return _e == other._e; }
    template <class OGraph>
    bool operator!=(const PythonEdge<OGraph>& other) const { return !(*this == other); }
    template <class OGraph>
    bool operator<(const PythonEdge<OGraph>& other)  const
Tiago Peixoto's avatar
Tiago Peixoto committed
388
    {
389
390
        check_valid();
        other.check_valid();
391
392
        Graph& g = *std::shared_ptr<Graph>(_g);
        OGraph& og = *std::shared_ptr<OGraph>(other._g);
393
394
395
        auto eindex = get(boost::edge_index_t(), g);
        auto eindex2 = get(boost::edge_index_t(), og);
        return eindex[_e] < eindex2[other._e];
Tiago Peixoto's avatar
Tiago Peixoto committed
396
    }
397
398
399
400
401
402
    template <class OGraph>
    bool operator<=(const PythonEdge<OGraph>& other) const {return *this < other || *this == other;}
    template <class OGraph>
    bool operator>(const PythonEdge<OGraph>& other) const {return !(*this < other || *this == other);}
    template <class OGraph>
    bool operator>=(const PythonEdge<OGraph>& other) const {return *this > other || *this == other;}
Tiago Peixoto's avatar
Tiago Peixoto committed
403

404
private:
405
    std::weak_ptr<Graph> _g;
406
    edge_descriptor _e;
407
408
409

    template <class OGraph>
    friend class PythonEdge;
410
411
};

412
413
414
415
416
417
418
419
420
421
// 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
422
423
424
425
426
427
428
429
        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
430
                >::type,
Tiago Peixoto's avatar
Tiago Peixoto committed
431
432
            boost::mpl::bool_<true>,
            boost::mpl::bool_<false> >::type type;
433
434
435
436
    };
};

template <class PropertyMap>
437
438
439
class PythonPropertyMap
{
public:
440
441
    PythonPropertyMap(const PropertyMap& pmap)
        : _pmap(pmap) {}
442

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

Tiago Peixoto's avatar
Tiago Peixoto committed
445
    typedef typename boost::mpl::if_<
446
447
448
449
        typename return_reference::apply<value_type>::type,
        value_type&,
        value_type>::type reference;

450
    template <class PythonDescriptor>
451
    reference get_value(const PythonDescriptor& key)
452
    {
453
454
        key.check_valid();
        return get(_pmap, key.get_descriptor());
455
456
    }

457
458
    // in this case, val should be a copy, not a reference. This is to avoid a
    // problem with vector-valued property maps
459
    template <class PythonDescriptor>
460
    void set_value(const PythonDescriptor& key, value_type val)
461
    {
462
463
464
        set_value_dispatch(key, val,
                           std::is_convertible<typename boost::property_traits<PropertyMap>::category,
                                               boost::writable_property_map_tag>());
465
466
    }

467
    template <class PythonDescriptor>
468
469
    void set_value_dispatch(const PythonDescriptor& key, const value_type& val,
                            std::true_type)
470
    {
471
472
        key.check_valid();
        put(_pmap, key.get_descriptor(), val);
473
474
    }

475
    template <class PythonDescriptor>
476
477
    void set_value_dispatch(const PythonDescriptor&, const value_type&,
                            std::false_type)
478
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
479
        throw ValueException("property is read-only");
480
481
    }

482
    size_t get_hash() const
483
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
484
        return std::hash<size_t>()(size_t(this));
485
486
    }

487
    std::string get_type() const
488
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
489
490
491
        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)
492
493
            return gcc_demangle(typeid(value_type).name());
        else
Tiago Peixoto's avatar
Tiago Peixoto committed
494
            return type_names[boost::mpl::find<value_types,
495
                                               value_type>::type::pos::value];
496
497
    }

498
    boost::any get_map() const
499
500
501
502
    {
        return _pmap;
    }

503
    boost::any get_dynamic_map() const
504
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
505
        return (boost::dynamic_property_map*)
506
507
508
509
            (new boost::detail::dynamic_property_map_adaptor<PropertyMap>
             (_pmap));
    }

510
    boost::python::object get_array(size_t size)
511
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
512
513
514
515
516
517
518
519
        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 >
520
            ::type>::type isnt_vector_map;
521
        return get_array_dispatch(size, isnt_vector_map());
522
523
    }

524
    boost::python::object get_array_dispatch(size_t size, boost::mpl::bool_<false>)
525
    {
526
        _pmap.resize(size);
527
528
529
        return wrap_vector_not_owned(_pmap.get_storage());
    }

530
    boost::python::object get_array_dispatch(size_t, boost::mpl::bool_<true>)
531
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
532
        return boost::python::object();
533
534
    }

535
    bool is_writable() const
536
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
537
538
        return std::is_convertible<typename boost::property_traits<PropertyMap>::category,
                                   boost::writable_property_map_tag>::value;
539
540
    }

541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
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
    void reserve(size_t size)
    {
        typename boost::mpl::or_<
            std::is_same<PropertyMap,
                         GraphInterface::vertex_index_map_t>,
            std::is_same<PropertyMap,
                         GraphInterface::edge_index_map_t> >::type is_index;
        reserve_dispatch(size, is_index);
    }

    void reserve_dispatch(size_t size, boost::mpl::bool_<false>)
    {
        _pmap.reserve(size);
    }

    void reserve_dispatch(size_t, boost::mpl::bool_<true>)
    {
    }

    void resize(size_t size)
    {
        typename boost::mpl::or_<
            std::is_same<PropertyMap,
                         GraphInterface::vertex_index_map_t>,
            std::is_same<PropertyMap,
                         GraphInterface::edge_index_map_t> >::type is_index;
        resize_dispatch(size, is_index);
    }

    void resize_dispatch(size_t size, boost::mpl::bool_<false>)
    {
        _pmap.resize(size);
    }

    void resize_dispatch(size_t, boost::mpl::bool_<true>)
    {
    }

    void shrink_to_fit()
    {
        typename boost::mpl::or_<
            std::is_same<PropertyMap,
                         GraphInterface::vertex_index_map_t>,
            std::is_same<PropertyMap,
                         GraphInterface::edge_index_map_t> >::type is_index;
        shrink_to_fit_dispatch(is_index);
    }

    void shrink_to_fit_dispatch(boost::mpl::bool_<false>)
    {
        _pmap.shrink_to_fit();
    }

    void shrink_to_fit_dispatch(boost::mpl::bool_<true>)
    {
    }

598
private:
599
    PropertyMap _pmap; // hold an internal copy, since it's cheap
600
601
602
};


603
604
605
606
607
608
609
610
//
// 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
611
                     boost::any pmap, boost::python::object& new_prop, bool& found) const
612
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
613
        size_t i = boost::mpl::find<value_types,ValueType>::type::pos::value;
614
615
        if (type_name == type_names[i])
        {
616
617
            typedef typename property_map_type::apply<ValueType, IndexMap>::type
                map_t;
618
619
620
621
            map_t prop;
            if (pmap.empty())
                prop = map_t(index);
            else
Tiago Peixoto's avatar
Tiago Peixoto committed
622
                prop = boost::any_cast<map_t>(pmap);
623

Tiago Peixoto's avatar
Tiago Peixoto committed
624
            new_prop = boost::python::object(PythonPropertyMap<map_t>(prop));
625
626
627
628
629
630
            found = true;
        }
    }
};

template <class IndexMap>
Tiago Peixoto's avatar
Tiago Peixoto committed
631
632
boost::python::object new_property(const string& type, IndexMap index_map,
                                   boost::any pmap)
633
{
Tiago Peixoto's avatar
Tiago Peixoto committed
634
    boost::python::object prop;
635
    bool found = false;
Tiago Peixoto's avatar
Tiago Peixoto committed
636
637
638
639
    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)));
640
    if (!found)
Tiago Peixoto's avatar
Tiago Peixoto committed
641
        throw ValueException("Invalid property type: " + type);
642
643
644
    return prop;
}

645
646
647
648
649
650
651
652
653
//
// Python IO streams (minimal access to c++ streams)
//

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

654
    void write(const std::string& s, size_t n)
655
656
657
658
    {
        _s.write(s.c_str(), long(n));
    }

659
    void flush()
660
661
662
663
664
665
666
667
668
669
670
671
672
    {
        _s.flush();
    }

private:
    std::ostream& _s;
};

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

673
    boost::python::object read(size_t n)
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
    {
        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;
};


696
697
698
} //graph_tool namespace

#endif