graph_python_interface.hh 20.5 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 "demangle.hh"
48
#include "numpy_bind.hh"
49

50
51
52
#ifdef HAVE_BOOST_COROUTINE
#include <boost/coroutine/all.hpp>
#endif // HAVE_BOOST_COROUTINE
53

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

60
61
62
namespace graph_tool
{

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

84
85
86
87
88
89
90
91
92
93
94
95
#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)),
96
          _iter(begin(*_coro)), _end(end(*_coro)), _first(true) {}
97
98
    boost::python::object next()
    {
99
100
101
102
        if (_first)
            _first = false;
        else
            ++_iter;
103
104
105
106
107
108
109
110
111
        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;
112
    bool _first;
113
114
115
};

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

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

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

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

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

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

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

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

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

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

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

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

207

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

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

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

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

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

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

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

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

    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
        return name_demangle(typeid(Graph).name());
379
380
381
382
383
384
385
386
    }

    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
387
    {
388
389
        check_valid();
        other.check_valid();
390
391
        Graph& g = *std::shared_ptr<Graph>(_g);
        OGraph& og = *std::shared_ptr<OGraph>(other._g);
392
393
394
        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
395
    }
396
397
398
399
400
401
    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
402

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

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

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

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

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

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

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

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

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

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

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

486
    std::string get_type() const
487
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
488
489
        if (std::is_same<typename boost::mpl::find<value_types,value_type>::type,
                         typename boost::mpl::end<value_types>::type>::value)
490
            return name_demangle(typeid(value_type).name());
491
        else
Tiago Peixoto's avatar
Tiago Peixoto committed
492
            return type_names[boost::mpl::find<value_types,
493
                                               value_type>::type::pos::value];
494
495
    }

496
    boost::any get_map() const
497
498
499
500
    {
        return _pmap;
    }

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

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

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

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

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

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
    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>)
    {
    }

596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
    size_t data_ptr()
    {
        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;
        return data_ptr_dispatch(is_index);
    }

    size_t data_ptr_dispatch(boost::mpl::bool_<true>)
    {
        return 0;
    }

    size_t data_ptr_dispatch(boost::mpl::bool_<false>)
    {
        return size_t(_pmap.get_storage().data());
    }

616
private:
617
    PropertyMap _pmap; // hold an internal copy, since it's cheap
618
619
620
};


621
622
623
624
625
626
627
628
//
// 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
629
                     boost::any pmap, boost::python::object& new_prop, bool& found) const
630
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
631
        size_t i = boost::mpl::find<value_types,ValueType>::type::pos::value;
632
633
        if (type_name == type_names[i])
        {
634
635
            typedef typename property_map_type::apply<ValueType, IndexMap>::type
                map_t;
636
637
638
639
            map_t prop;
            if (pmap.empty())
                prop = map_t(index);
            else
Tiago Peixoto's avatar
Tiago Peixoto committed
640
                prop = boost::any_cast<map_t>(pmap);
641

Tiago Peixoto's avatar
Tiago Peixoto committed
642
            new_prop = boost::python::object(PythonPropertyMap<map_t>(prop));
643
644
645
646
647
648
            found = true;
        }
    }
};

template <class IndexMap>
Tiago Peixoto's avatar
Tiago Peixoto committed
649
650
boost::python::object new_property(const string& type, IndexMap index_map,
                                   boost::any pmap)
651
{
Tiago Peixoto's avatar
Tiago Peixoto committed
652
    boost::python::object prop;
653
    bool found = false;
Tiago Peixoto's avatar
Tiago Peixoto committed
654
655
656
657
    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)));
658
    if (!found)
Tiago Peixoto's avatar
Tiago Peixoto committed
659
        throw ValueException("Invalid property type: " + type);
660
661
662
    return prop;
}

663
664
665
666
667
668
669
670
671
//
// Python IO streams (minimal access to c++ streams)
//

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

672
    void write(const std::string& s, size_t n)
673
674
675
676
    {
        _s.write(s.c_str(), long(n));
    }

677
    void flush()
678
679
680
681
682
683
684
685
686
687
688
689
690
    {
        _s.flush();
    }

private:
    std::ostream& _s;
};

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

691
    boost::python::object read(size_t n)
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
    {
        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;
};


714
715
716
} //graph_tool namespace

#endif