graph_python_interface.hh 21.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-2020 Tiago de Paula Peixoto <tiago@skewed.de>
4
//
5
6
7
8
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option) any
// later version.
9
//
10
11
12
13
// 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 Lesser General Public License for more
// details.
14
//
15
// You should have received a copy of the GNU Lesser 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
#include <boost/python.hpp>

Tiago Peixoto's avatar
Tiago Peixoto committed
23
24
25
26
27
28
29
namespace std
{
    template<>
    struct hash<boost::python::object>
    {
        size_t operator()(const boost::python::object& o) const
        {
30
            return std::hash<int64_t>()(boost::python::extract<int64_t>(o.attr("__hash__")()));
Tiago Peixoto's avatar
Tiago Peixoto committed
31
32
33
34
        }
    };
}

35
36
#include <boost/graph/graph_traits.hpp>

37
38
39
40
41
42
#include <boost/version.hpp>
#if (BOOST_VERSION >= 104000)
#   include <boost/property_map/dynamic_property_map.hpp>
#else
#   include <boost/dynamic_property_map.hpp>
#endif
Tiago Peixoto's avatar
Tiago Peixoto committed
43

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
        : _coro(make_coro<coro_t::pull_type>(dispatch)),
94
          _iter(begin(*_coro)), _end(end(*_coro)), _first(true) {}
95
96
    boost::python::object next()
    {
97
98
99
100
        if (_first)
            _first = false;
        else
            ++_iter;
101
102
103
104
105
106
107
108
109
        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;
110
    bool _first;
111
112
113
};

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

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

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

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

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

138
    void check_valid() const
139
    {
140
        if (!is_valid())
141
            throw ValueException("invalid vertex descriptor: " +
142
                                 boost::lexical_cast<std::string>(_v));
143
    }
144

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

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

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

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

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

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

205

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

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

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

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

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

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

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

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

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

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

294
// below are classes related to the PythonEdge type
295

Tiago Peixoto's avatar
Tiago Peixoto committed
296
297
298
299
300
301
302
303
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;
};
304

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

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

320
321
        auto s = source(_e, g);
        auto t = target(_e, g);
322

323
        return ((s < num_vertices(g)) && (t < num_vertices(g)));
324
325
    }

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

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

    virtual GraphInterface::edge_t get_descriptor() const
338
339
340
341
    {
        return _e;
    }

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

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

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

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

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

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

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

413
private:
414
    std::weak_ptr<Graph> _g;
415
    edge_descriptor _e;
416
417
418

    template <class OGraph>
    friend class PythonEdge;
419
420
};

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

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

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

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

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

466
    reference get_value_int(size_t v)
467
    {
468
        return get(_pmap, v);
469
470
    }

471
472
    // in this case, val should be a copy, not a reference. This is to avoid a
    // problem with vector-valued property maps
473
    template <class PythonDescriptor>
474
    void set_value(const PythonDescriptor& key, value_type val)
475
    {
476
477
478
479
480
        if constexpr (std::is_convertible<typename boost::property_traits<PropertyMap>::category,
                                          boost::writable_property_map_tag>::value)
            put(_pmap, key.get_descriptor(), val);
        else
            throw ValueException("property is read-only");
481
482
    }

483
    void set_value_int(size_t v, value_type val)
484
    {
485
486
487
488
489
        if constexpr (std::is_convertible<typename boost::property_traits<PropertyMap>::category,
                                          boost::writable_property_map_tag>::value)
            put(_pmap, v, val);
        else
            throw ValueException("property is read-only");
490
491
    }

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

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

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

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

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

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

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

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

Tiago Peixoto's avatar
Tiago Peixoto committed
607
608
609
610
611
612
613
614
615
616
617
618
    void swap(PythonPropertyMap& other)
    {
        swap_dispatch(other,
                      std::is_convertible<typename boost::property_traits<PropertyMap>::category,
                                          boost::writable_property_map_tag>());
    }

    void swap_dispatch(PythonPropertyMap& other, std::true_type)
    {
        _pmap.swap(other._pmap);
    }

Tiago Peixoto's avatar
Tiago Peixoto committed
619
    void swap_dispatch(PythonPropertyMap&, std::false_type)
Tiago Peixoto's avatar
Tiago Peixoto committed
620
621
622
623
    {
        throw ValueException("Read-only property map cannot be swapped.");
    }

624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
    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());
    }

644
private:
645
    PropertyMap _pmap; // hold an internal copy, since it's cheap
646
647
648
};


649
650
651
652
653
654
655
//
// Create new properties
//

struct new_property_map
{
    template <class ValueType, class IndexMap>
656
657
    void operator()(ValueType, IndexMap index, const std::string& type_name,
                    boost::any pmap, boost::python::object& new_prop, bool& found) const
658
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
659
        size_t i = boost::mpl::find<value_types,ValueType>::type::pos::value;
660
661
        if (type_name == type_names[i])
        {
662
663
            typedef typename property_map_type::apply<ValueType, IndexMap>::type
                map_t;
664
665
666
667
            map_t prop;
            if (pmap.empty())
                prop = map_t(index);
            else
Tiago Peixoto's avatar
Tiago Peixoto committed
668
                prop = boost::any_cast<map_t>(pmap);
669

Tiago Peixoto's avatar
Tiago Peixoto committed
670
            new_prop = boost::python::object(PythonPropertyMap<map_t>(prop));
671
672
673
674
675
676
            found = true;
        }
    }
};

template <class IndexMap>
677
boost::python::object new_property(const std::string& type, IndexMap index_map,
Tiago Peixoto's avatar
Tiago Peixoto committed
678
                                   boost::any pmap)
679
{
Tiago Peixoto's avatar
Tiago Peixoto committed
680
    boost::python::object prop;
681
    bool found = false;
682
683
684
685
686
687
    boost::mpl::for_each<value_types>(
        [&](auto t)
        {
            return new_property_map()(t, index_map, type, pmap, prop, found);
        });

688
    if (!found)
Tiago Peixoto's avatar
Tiago Peixoto committed
689
        throw ValueException("Invalid property type: " + type);
690
691
692
    return prop;
}

693
694
695
696
697
698
699
700
701
//
// Python IO streams (minimal access to c++ streams)
//

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

702
    void write(const std::string& s, size_t n)
703
704
705
706
    {
        _s.write(s.c_str(), long(n));
    }

707
    void flush()
708
709
710
711
712
713
714
715
716
717
718
719
720
    {
        _s.flush();
    }

private:
    std::ostream& _s;
};

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

721
    boost::python::object read(size_t n)
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
    {
        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;
};

743
bool hasattr(boost::python::object obj, std::string const& attrName);
744

745
746
747
} //graph_tool namespace

#endif