graph_python_interface.hh 20.9 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-2017 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
#include "coroutine.hh"
50

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

57
58
59
namespace graph_tool
{

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

81
82
83
84
#ifdef HAVE_BOOST_COROUTINE

// generic coroutine generator adaptor

85
86
typedef graph_tool::coroutines::asymmetric_coroutine<boost::python::object>
   coro_t;
87
88
89
90
91
92

class CoroGenerator
{
public:
    template <class Dispatch>
    CoroGenerator(Dispatch& dispatch)
93
94
95
        : _coro(std::make_shared<coro_t::pull_type>
                (coroutines::fixedsize_stack(BOOST_COROUTINE_STACK_SIZE),
                 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: " +
144
                                 boost::lexical_cast<std::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

Tiago Peixoto's avatar
Tiago Peixoto committed
298
299
300
301
302
303
304
305
class EdgeBase // useful to unite all edge types
{
public:
    virtual bool is_valid() const = 0;
    virtual void check_valid() const = 0;
    virtual void invalidate() = 0;
    virtual GraphInterface::edge_t get_descriptor() const = 0;
};
306

307
template <class Graph>
308
class PythonEdge : public EdgeBase
309
310
{
public:
Tiago Peixoto's avatar
Tiago Peixoto committed
311
    typedef typename boost::graph_traits<Graph>::edge_descriptor edge_descriptor;
312
    PythonEdge(std::weak_ptr<Graph> g, edge_descriptor e)
313
        : _g(g), _e(e) {}
314

Tiago Peixoto's avatar
Tiago Peixoto committed
315
    virtual bool is_valid() const
Tiago Peixoto's avatar
Tiago Peixoto committed
316
    {
317
        if (_g.expired())
318
            return false;
319
320
        std::shared_ptr<Graph> gp = _g.lock();
        Graph& g = *gp.get();
321

322
323
        auto s = source(_e, g);
        auto t = target(_e, g);
324

325
        return ((s < num_vertices(g)) && (t < num_vertices(g)));
326
327
    }

Tiago Peixoto's avatar
Tiago Peixoto committed
328
    virtual void check_valid() const
329
    {
330
        if (!is_valid())
Tiago Peixoto's avatar
Tiago Peixoto committed
331
            throw ValueException("invalid edge descriptor");
Tiago Peixoto's avatar
Tiago Peixoto committed
332
333
    }

Tiago Peixoto's avatar
Tiago Peixoto committed
334
335
336
337
338
339
    virtual void invalidate()
    {
        _g.reset();
    }

    virtual GraphInterface::edge_t get_descriptor() const
340
341
342
343
    {
        return _e;
    }

344
    PythonVertex<Graph> get_source() const
345
    {
346
        check_valid();
347
348
349
        std::shared_ptr<Graph> gp = _g.lock();
        Graph& g = *gp.get();
        return PythonVertex<Graph>(gp, source(_e, g));
350
    }
351

352
    PythonVertex<Graph> get_target() const
Tiago Peixoto's avatar
Tiago Peixoto committed
353
    {
354
        check_valid();
355
356
357
        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
358
359
    }

360
    std::string get_string() const
361
    {
362
        check_valid();
363
364
        std::shared_ptr<Graph> gp = _g.lock();
        Graph& g = *gp.get();
365
366
367
368
369
        auto s = source(_e, g);
        auto t = target(_e, g);
        return "(" + boost::lexical_cast<std::string>(s) + ", "
            + boost::lexical_cast<std::string>(t) + ")";
    }
370

371
    size_t get_hash() const
Tiago Peixoto's avatar
Tiago Peixoto committed
372
    {
373
        check_valid();
374
375
        std::shared_ptr<Graph> gp = _g.lock();
        Graph& g = *gp.get();
376
377
        auto eindex = get(boost::edge_index_t(), g);
        return std::hash<size_t>()(eindex[_e]);
Tiago Peixoto's avatar
Tiago Peixoto committed
378
379
    }

380
    size_t get_graph_ptr() const
381
    {
382
383
384
385
        if (_g.expired())
            return 0;
        std::shared_ptr<Graph> gp = _g.lock();
        return size_t(gp.get());
386
387
    }

388
    std::string get_graph_type() const
389
    {
390
        return name_demangle(typeid(Graph).name());
391
392
393
394
395
396
397
398
    }

    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
399
    {
400
401
        check_valid();
        other.check_valid();
402
403
        Graph& g = *std::shared_ptr<Graph>(_g);
        OGraph& og = *std::shared_ptr<OGraph>(other._g);
404
405
406
        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
407
    }
408
409
410
411
412
413
    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
414

415
private:
416
    std::weak_ptr<Graph> _g;
417
    edge_descriptor _e;
418
419
420

    template <class OGraph>
    friend class PythonEdge;
421
422
};

423
424
425
426
427
428
429
430
431
432
// 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
433
434
435
436
437
        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,
438
                                                           std::string> >::type,
Tiago Peixoto's avatar
Tiago Peixoto committed
439
440
                    typename boost::mpl::not_<std::is_same<ValueType,
                                                           boost::python::object> >::type>::type
441
                >::type,
Tiago Peixoto's avatar
Tiago Peixoto committed
442
443
            boost::mpl::bool_<true>,
            boost::mpl::bool_<false> >::type type;
444
445
446
447
    };
};

template <class PropertyMap>
448
449
450
class PythonPropertyMap
{
public:
451
452
    PythonPropertyMap(const PropertyMap& pmap)
        : _pmap(pmap) {}
453

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

Tiago Peixoto's avatar
Tiago Peixoto committed
456
    typedef typename boost::mpl::if_<
457
458
459
460
        typename return_reference::apply<value_type>::type,
        value_type&,
        value_type>::type reference;

461
    template <class PythonDescriptor>
462
    reference get_value(const PythonDescriptor& key)
463
    {
464
465
        key.check_valid();
        return get(_pmap, key.get_descriptor());
466
467
    }

468
469
    // in this case, val should be a copy, not a reference. This is to avoid a
    // problem with vector-valued property maps
470
    template <class PythonDescriptor>
471
    void set_value(const PythonDescriptor& key, value_type val)
472
    {
473
474
475
        set_value_dispatch(key, val,
                           std::is_convertible<typename boost::property_traits<PropertyMap>::category,
                                               boost::writable_property_map_tag>());
476
477
    }

478
    template <class PythonDescriptor>
479
480
    void set_value_dispatch(const PythonDescriptor& key, const value_type& val,
                            std::true_type)
481
    {
482
483
        key.check_valid();
        put(_pmap, key.get_descriptor(), val);
484
485
    }

486
    template <class PythonDescriptor>
487
488
    void set_value_dispatch(const PythonDescriptor&, const value_type&,
                            std::false_type)
489
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
490
        throw ValueException("property is read-only");
491
492
    }

493
    size_t get_hash() const
494
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
495
        return std::hash<size_t>()(size_t(this));
496
497
    }

498
    std::string get_type() const
499
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
500
501
        if (std::is_same<typename boost::mpl::find<value_types,value_type>::type,
                         typename boost::mpl::end<value_types>::type>::value)
502
            return name_demangle(typeid(value_type).name());
503
        else
Tiago Peixoto's avatar
Tiago Peixoto committed
504
            return type_names[boost::mpl::find<value_types,
505
                                               value_type>::type::pos::value];
506
507
    }

508
    boost::any get_map() const
509
510
511
512
    {
        return _pmap;
    }

513
    boost::any get_dynamic_map() const
514
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
515
        return (boost::dynamic_property_map*)
516
517
518
519
            (new boost::detail::dynamic_property_map_adaptor<PropertyMap>
             (_pmap));
    }

520
    boost::python::object get_array(size_t size)
521
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
522
523
524
525
526
527
528
529
        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 >
530
            ::type>::type isnt_vector_map;
531
        return get_array_dispatch(size, isnt_vector_map());
532
533
    }

534
    boost::python::object get_array_dispatch(size_t size, boost::mpl::bool_<false>)
535
    {
536
        _pmap.resize(size);
537
538
539
        return wrap_vector_not_owned(_pmap.get_storage());
    }

540
    boost::python::object get_array_dispatch(size_t, boost::mpl::bool_<true>)
541
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
542
        return boost::python::object();
543
544
    }

545
    bool is_writable() const
546
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
547
548
        return std::is_convertible<typename boost::property_traits<PropertyMap>::category,
                                   boost::writable_property_map_tag>::value;
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
598
599
600
601
602
603
604
605
606
607
    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>)
    {
    }

608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
    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());
    }

628
private:
629
    PropertyMap _pmap; // hold an internal copy, since it's cheap
630
631
632
};


633
634
635
636
637
638
639
//
// Create new properties
//

struct new_property_map
{
    template <class ValueType, class IndexMap>
640
641
    void operator()(ValueType, IndexMap index, const std::string& type_name,
                    boost::any pmap, boost::python::object& new_prop, bool& found) const
642
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
643
        size_t i = boost::mpl::find<value_types,ValueType>::type::pos::value;
644
645
        if (type_name == type_names[i])
        {
646
647
            typedef typename property_map_type::apply<ValueType, IndexMap>::type
                map_t;
648
649
650
651
            map_t prop;
            if (pmap.empty())
                prop = map_t(index);
            else
Tiago Peixoto's avatar
Tiago Peixoto committed
652
                prop = boost::any_cast<map_t>(pmap);
653

Tiago Peixoto's avatar
Tiago Peixoto committed
654
            new_prop = boost::python::object(PythonPropertyMap<map_t>(prop));
655
656
657
658
659
660
            found = true;
        }
    }
};

template <class IndexMap>
661
boost::python::object new_property(const std::string& type, IndexMap index_map,
Tiago Peixoto's avatar
Tiago Peixoto committed
662
                                   boost::any pmap)
663
{
Tiago Peixoto's avatar
Tiago Peixoto committed
664
    boost::python::object prop;
665
    bool found = false;
Tiago Peixoto's avatar
Tiago Peixoto committed
666
667
668
669
    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)));
670
    if (!found)
Tiago Peixoto's avatar
Tiago Peixoto committed
671
        throw ValueException("Invalid property type: " + type);
672
673
674
    return prop;
}

675
676
677
678
679
680
681
682
683
//
// Python IO streams (minimal access to c++ streams)
//

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

684
    void write(const std::string& s, size_t n)
685
686
687
688
    {
        _s.write(s.c_str(), long(n));
    }

689
    void flush()
690
691
692
693
694
695
696
697
698
699
700
701
702
    {
        _s.flush();
    }

private:
    std::ostream& _s;
};

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

703
    boost::python::object read(size_t n)
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
    {
        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;
};


726
727
728
} //graph_tool namespace

#endif