Commit c6c01d62 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Edge and vertex descriptors now carry a weakref to their Graphs

This fixes an obvious problem, where the graph gets deleted, and the
descriptors are still lying around. Usage of orphaned descriptors will
now just raise a ValueError.

The __repr__ function of Edge, Vertex, and PropertyMap now give
something more informative about each object.
parent 06b2914a
......@@ -104,23 +104,12 @@ public:
//
// python interface
//
python::object Vertices() const;
python::object Vertex(size_t i) const;
python::object Edges() const;
python::object AddVertex();
void RemoveVertex(const python::object& v);
python::object AddEdge(const python::object& s, const python::object& t);
void RemoveEdge(const python::object& e);
python::object DegreeMap(string deg) const;
// used for graph properties
graph_property_tag GetDescriptor() const { return graph_property_tag(); }
bool CheckValid() const {return true;}
void ExportPythonInterface() const;
// I/O
void WriteToFile(string s, python::object pf, string format,
python::list properties);
......
......@@ -305,13 +305,14 @@ void ungroup_vector_property(GraphInterface& g, boost::any vector_prop,
boost::any prop, size_t pos, bool edge);
void group_vector_property(GraphInterface& g, boost::any vector_prop,
boost::any prop, size_t pos, bool edge);
void export_python_interface();
BOOST_PYTHON_MODULE(libgraph_tool_core)
{
// numpy
import_array();
GraphInterface().ExportPythonInterface();
export_python_interface();
register_exception_translator<GraphException>
(graph_exception_translator<GraphException>);
......@@ -348,13 +349,6 @@ BOOST_PYTHON_MODULE(libgraph_tool_core)
.def("ShiftVertexProperty", &GraphInterface::ShiftVertexProperty)
.def("WriteToFile", &GraphInterface::WriteToFile)
.def("ReadFromFile",&GraphInterface::ReadFromFile)
.def("Vertices", &GraphInterface::Vertices)
.def("Vertex", &GraphInterface::Vertex)
.def("Edges", &GraphInterface::Edges)
.def("AddVertex", &GraphInterface::AddVertex)
.def("AddEdge", &GraphInterface::AddEdge)
.def("RemoveVertex", &GraphInterface::RemoveVertex)
.def("RemoveEdge", &GraphInterface::RemoveEdge)
.def("DegreeMap", &GraphInterface::DegreeMap)
.def("Clear", &GraphInterface::Clear)
.def("ClearEdges", &GraphInterface::ClearEdges)
......
......@@ -26,26 +26,28 @@ using namespace std;
using namespace boost;
using namespace graph_tool;
namespace graph_tool
{
struct get_vertex_iterator
{
template <class Graph>
void operator()(Graph& g, GraphInterface& gi,
void operator()(Graph& g, python::object& pg,
python::object& iter) const
{
typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator;
iter = python::object(PythonIterator<PythonVertex,
vertex_iterator>(gi, vertices(g)));
vertex_iterator>(pg, vertices(g)));
}
};
python::object
GraphInterface::Vertices() const
python::object get_vertices(python::object g)
{
GraphInterface& gi = python::extract<GraphInterface&>(g());
python::object iter;
run_action<>()(const_cast<GraphInterface&>(*this),
bind<void>(get_vertex_iterator(), _1,
ref(const_cast<GraphInterface&>(*this)),
ref(iter)))();
run_action<>()(gi, bind<void>(get_vertex_iterator(), _1,
ref(g),
ref(iter)))();
return iter;
}
......@@ -53,17 +55,17 @@ struct get_vertex_soft
{
template <class Graph>
void operator()(Graph& g,
GraphInterface& gi,
python::object& pg,
size_t i, python::object& v) const
{
v = python::object(PythonVertex(gi, vertex(i, g)));
v = python::object(PythonVertex(pg, vertex(i, g)));
}
};
struct get_vertex_hard
{
template <class Graph>
void operator()(Graph& g, GraphInterface& gi, size_t i,
void operator()(Graph& g, python::object& pg, size_t i,
python::object& v) const
{
size_t c = 0;
......@@ -72,59 +74,54 @@ struct get_vertex_hard
{
if (c == i)
{
v = python::object(PythonVertex(gi, *vi));
v = python::object(PythonVertex(pg, *vi));
return;
}
++c;
}
v = python::object(PythonVertex(gi,
v = python::object(PythonVertex(pg,
graph_traits<Graph>::null_vertex()));
}
};
python::object
GraphInterface::Vertex(size_t i) const
python::object get_vertex(python::object g, size_t i)
{
GraphInterface& gi = python::extract<GraphInterface&>(g());
python::object v;
if (IsVertexFilterActive())
run_action<>()(const_cast<GraphInterface&>(*this),
bind<void>(get_vertex_hard(), _1,
ref(const_cast<GraphInterface&>(*this)),
i, ref(v)))();
if (gi.IsVertexFilterActive())
run_action<>()(gi,
bind<void>(get_vertex_hard(), _1, ref(g), i, ref(v)))();
else
run_action<>()(const_cast<GraphInterface&>(*this),
bind<void>(get_vertex_soft(), _1,
ref(const_cast<GraphInterface&>(*this)), i,
ref(v)))();
run_action<>()(gi,
bind<void>(get_vertex_soft(), _1, ref(g), i, ref(v)))();
return v;
}
struct get_edge_iterator
{
template <class Graph>
void operator()(Graph& g, GraphInterface& gi,
python::object& iter) const
void operator()(Graph& g, const python::object& pg, python::object& iter)
const
{
typedef typename graph_traits<Graph>::edge_iterator edge_iterator;
iter = python::object(PythonIterator<PythonEdge<Graph>,
edge_iterator>(gi, edges(g)));
edge_iterator>(pg, edges(g)));
}
};
python::object
GraphInterface::Edges() const
python::object get_edges(python::object g)
{
GraphInterface& gi = python::extract<GraphInterface&>(g());
python::object iter;
run_action<>()(const_cast<GraphInterface&>(*this),
bind<void>(get_edge_iterator(), _1,
ref(const_cast<GraphInterface&>(*this)),
ref(iter)))();
run_action<>()(gi,
bind<void>(get_edge_iterator(), _1, ref(g), ref(iter)))();
return iter;
}
python::object GraphInterface::AddVertex()
python::object add_vertex(python::object g)
{
return python::object(PythonVertex(*this, add_vertex(_mg)));
GraphInterface& gi = python::extract<GraphInterface&>(g());
return python::object(PythonVertex(g, add_vertex(gi.GetGraph())));
}
struct shift_vertex_property
......@@ -143,28 +140,28 @@ struct shift_vertex_property
}
};
void GraphInterface::RemoveVertex(const python::object& v)
void remove_vertex(GraphInterface& gi, const python::object& v)
{
PythonVertex& pv = python::extract<PythonVertex&>(v);
pv.CheckValid();
vertex_t dv = pv.GetDescriptor();
GraphInterface::vertex_t dv = pv.GetDescriptor();
pv.SetValid(false);
//remove vertex
clear_vertex(dv, _mg);
remove_vertex(dv, _mg);
clear_vertex(dv, gi.GetGraph());
remove_vertex(dv, gi.GetGraph());
}
struct add_new_edge
{
template <class Graph, class EdgeIndexMap>
void operator()(Graph& g, GraphInterface& gi, const PythonVertex& s,
const PythonVertex& t, EdgeIndexMap edge_index,
python::object& new_e) const
void operator()(Graph& g, python::object& pg, GraphInterface& gi,
const PythonVertex& s, const PythonVertex& t,
EdgeIndexMap edge_index, python::object& new_e) const
{
typename graph_traits<Graph>::edge_descriptor e =
add_edge(s.GetDescriptor(), t.GetDescriptor(), g).first;
new_e = python::object(PythonEdge<Graph>(gi, e));
new_e = python::object(PythonEdge<Graph>(pg, e));
gi.AddEdgeIndex(e);
}
};
......@@ -184,18 +181,17 @@ void GraphInterface::AddEdgeIndex(const edge_t& e)
_nedges++;
}
python::object GraphInterface::AddEdge(const python::object& s,
const python::object& t)
python::object add_edge(python::object g, const python::object& s,
const python::object& t)
{
PythonVertex& src = python::extract<PythonVertex&>(s);
PythonVertex& tgt = python::extract<PythonVertex&>(t);
src.CheckValid();
tgt.CheckValid();
GraphInterface& gi = python::extract<GraphInterface&>(g());
python::object new_e;
run_action<>()(*this, bind<void>(add_new_edge(), _1,
ref(*this), src, tgt,
_edge_index,
ref(new_e)))();
run_action<>()(gi, bind<void>(add_new_edge(), _1, ref(g), ref(gi), src, tgt,
gi.GetEdgeIndex(), ref(new_e)))();
return new_e;
}
......@@ -204,8 +200,7 @@ struct get_edge_descriptor
template <class Graph>
void operator()(const Graph& g, const python::object& e,
typename GraphInterface::edge_t& edge,
bool& found)
const
bool& found) const
{
PythonEdge<Graph>& pe = python::extract<PythonEdge<Graph>&>(e);
pe.CheckValid();
......@@ -215,17 +210,15 @@ struct get_edge_descriptor
}
};
void GraphInterface::RemoveEdge(const python::object& e)
void remove_edge(GraphInterface& gi, const python::object& e)
{
edge_t de;
GraphInterface::edge_t de;
bool found = false;
run_action<>()(*this,
bind<void>(get_edge_descriptor(), _1,
ref(e), ref(de),
ref(found)))();
run_action<>()(gi, bind<void>(get_edge_descriptor(), _1, ref(e), ref(de),
ref(found)))();
if (!found)
throw ValueException("invalid edge descriptor");
RemoveEdgeIndex(de);
gi.RemoveEdgeIndex(de);
}
void GraphInterface::RemoveEdgeIndex(const edge_t& e)
......@@ -364,8 +357,6 @@ struct export_python_interface
}
};
void export_python_properties(GraphInterface&);
PythonPropertyMap<GraphInterface::vertex_index_map_t>
get_vertex_index(GraphInterface& g)
{
......@@ -380,9 +371,13 @@ get_edge_index(GraphInterface& g)
(g.GetEdgeIndex());
}
} // namespace graph_tool
// register everything
void GraphInterface::ExportPythonInterface() const
void export_python_properties();
void export_python_interface()
{
using namespace boost::python;
......@@ -407,15 +402,23 @@ void GraphInterface::ExportPythonInterface() const
set<string> v_iterators;
typedef mpl::transform<graph_tool::detail::all_graph_views,
mpl::quote1<add_pointer> >::type graph_views;
mpl::for_each<graph_views>(bind<void>(export_python_interface(),
mpl::for_each<graph_views>(bind<void>(graph_tool::export_python_interface(),
_1,ref(v_iterators)));
export_python_properties(const_cast<GraphInterface&>(*this));
export_python_properties();
def("new_vertex_property",
&new_property<GraphInterface::vertex_index_map_t>);
def("new_edge_property", &new_property<GraphInterface::edge_index_map_t>);
def("new_graph_property",
&new_property<ConstantPropertyMap<size_t,graph_property_tag> >);
def("get_vertex", get_vertex);
def("get_vertices", get_vertices);
def("get_edges", get_edges);
def("add_vertex", graph_tool::add_vertex);
def("add_edge", graph_tool::add_edge);
def("remove_vertex", graph_tool::remove_vertex);
def("remove_edge", graph_tool::remove_edge);
def("get_vertex_index", get_vertex_index);
def("get_edge_index", get_edge_index);
}
......@@ -47,18 +47,21 @@ template <class Descriptor, class Iterator>
class PythonIterator
{
public:
PythonIterator(GraphInterface& gi, std::pair<Iterator,Iterator> e)
: _gi(gi), _e(e) {}
PythonIterator(const python::object& g, std::pair<Iterator,Iterator> e)
: _g(g), _e(e) {}
Descriptor Next()
{
if (_e.first == _e.second)
python::objects::stop_iteration_error();
Descriptor e(_gi, *_e.first);
if (_g() == python::object())
throw GraphException("The corresponding graph object has been"
" deleted during iteration!");
Descriptor e(_g, *_e.first);
++_e.first;
return e;
}
private:
GraphInterface& _gi;
python::object _g;
std::pair<Iterator,Iterator> _e;
};
......@@ -71,17 +74,20 @@ class PythonEdge;
class PythonVertex
{
public:
PythonVertex(GraphInterface& gi, GraphInterface::vertex_t v):
_gi(gi), _v(v), _valid(true)
PythonVertex(const python::object& g, GraphInterface::vertex_t v):
_g(g), _v(v), _valid(true)
{
CheckValid();
}
bool IsValid() const
{
if (_g().ptr() == Py_None)
return false;
GraphInterface& gi = python::extract<GraphInterface&>(_g());
return _valid &&
(_v != graph_traits<GraphInterface::multigraph_t>::null_vertex()) &&
(_v < num_vertices(_gi._mg));
(_v < num_vertices(gi._mg));
}
void SetValid(bool valid)
......@@ -115,19 +121,21 @@ public:
size_t GetInDegree() const
{
CheckValid();
GraphInterface& gi = python::extract<GraphInterface&>(_g());
size_t in_deg;
run_action<>()(_gi, bind<void>(get_degree<in_degreeS>(),
_1, _v,
ref(in_deg)))();
run_action<>()(gi, bind<void>(get_degree<in_degreeS>(),
_1, _v,
ref(in_deg)))();
return in_deg;
}
size_t GetOutDegree() const
{
CheckValid();
GraphInterface& gi = python::extract<GraphInterface&>(_g());
size_t out_deg;
run_action<>()(_gi, bind<void>(get_degree<out_degreeS>(), _1, _v,
ref(out_deg)))();
run_action<>()(gi, bind<void>(get_degree<out_degreeS>(), _1, _v,
ref(out_deg)))();
return out_deg;
}
......@@ -136,7 +144,7 @@ public:
struct get_out_edges
{
template<class Graph>
void operator()(const Graph& g, GraphInterface& gi,
void operator()(const Graph& g, const python::object& pg,
typename graph_traits<Graph>::vertex_descriptor v,
python::object& iter) const
{
......@@ -144,7 +152,7 @@ public:
out_edge_iterator;
iter = python::object(PythonIterator<PythonEdge<Graph>,
out_edge_iterator>
(gi, out_edges(v, g)));
(pg, out_edges(v, g)));
}
};
......@@ -152,16 +160,17 @@ public:
OutEdges() const
{
CheckValid();
GraphInterface& gi = python::extract<GraphInterface&>(_g());
python::object iter;
run_action<>()(_gi, bind<void>(get_out_edges(), _1,
ref(_gi), _v, ref(iter)))();
run_action<>()(gi, bind<void>(get_out_edges(), _1,
ref(_g), _v, ref(iter)))();
return iter;
}
struct get_in_edges
{
template<class Graph>
void operator()(const Graph& g, GraphInterface& gi,
void operator()(const Graph& g, const python::object& pg,
typename graph_traits<Graph>::vertex_descriptor v,
python::object& iter) const
{
......@@ -169,7 +178,7 @@ public:
in_edge_iterator;
iter = python::object
(PythonIterator<PythonEdge<Graph>,in_edge_iterator>
(gi, in_edge_iteratorS<Graph>::get_edges(v, g)));
(pg, in_edge_iteratorS<Graph>::get_edges(v, g)));
}
};
......@@ -177,9 +186,10 @@ public:
InEdges() const
{
CheckValid();
GraphInterface& gi = python::extract<GraphInterface&>(_g());
python::object iter;
run_action<>()(_gi, bind<void>(get_in_edges(), _1, ref(_gi),
_v, ref(iter)))();
run_action<>()(gi, bind<void>(get_in_edges(), _1, ref(_g), _v,
ref(iter)))();
return iter;
}
......@@ -191,12 +201,14 @@ public:
size_t GetHash() const
{
return hash<size_t>()(_gi._vertex_index[_v]);
return hash<size_t>()(_v);
}
size_t GetIndex() const
{
return _gi._vertex_index[_v];
CheckValid();
GraphInterface& gi = python::extract<GraphInterface&>(_g());
return gi._vertex_index[_v];
}
bool operator==(const PythonVertex& other) const
......@@ -214,7 +226,7 @@ public:
}
private:
GraphInterface& _gi;
python::object _g;
GraphInterface::vertex_t _v;
bool _valid;
};
......@@ -226,19 +238,22 @@ class PythonEdge
{
public:
typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor;
PythonEdge(GraphInterface& gi, edge_descriptor e)
: _gi(gi), _e(e), _valid(true)
PythonEdge(const python::object& g, edge_descriptor e)
: _g(g), _e(e), _valid(true)
{
CheckValid();
}
bool IsValid() const
{
if (_g().ptr() == Py_None)
return false;
GraphInterface& gi = python::extract<GraphInterface&>(_g());
return _valid &&
(_gi._vertex_index[target(_e, _gi._mg)] < num_vertices(_gi._mg)) &&
(_gi._vertex_index[source(_e, _gi._mg)] < num_vertices(_gi._mg)) &&
(target(_e, _gi._mg) != graph_traits<Graph>::null_vertex()) &&
(source(_e, _gi._mg) != graph_traits<Graph>::null_vertex());
(gi._vertex_index[target(_e, gi._mg)] < num_vertices(gi._mg)) &&
(gi._vertex_index[source(_e, gi._mg)] < num_vertices(gi._mg)) &&
(target(_e, gi._mg) != graph_traits<Graph>::null_vertex()) &&
(source(_e, gi._mg) != graph_traits<Graph>::null_vertex());
}
void SetValid(bool valid)
......@@ -260,46 +275,47 @@ public:
struct get_source
{
template<class GraphType>
void operator()(const GraphType& g, GraphInterface& gi,
void operator()(const GraphType& g, const python::object& pg,
const edge_descriptor& edge, python::object& vertex)
const
{
vertex = python::object(PythonVertex(gi, source(edge, g)));
vertex = python::object(PythonVertex(pg, source(edge, g)));
}
};
python::object GetSource() const
{
CheckValid();
GraphInterface& gi = python::extract<GraphInterface&>(_g());
python::object v;
run_action<>()(_gi, bind<void>(get_source(), _1, ref(_gi), ref(_e),
ref(v)))();
run_action<>()(gi, bind<void>(get_source(), _1, ref(_g), ref(_e),
ref(v)))();
return v;
}
struct get_target
{
template<class GraphType>
void operator()(const GraphType& g, GraphInterface& gi,
const edge_descriptor& edge,
python::object& vertex) const
void operator()(const GraphType& g, const python::object& pg,
const edge_descriptor& edge, python::object& vertex)
const
{
vertex = python::object(PythonVertex(gi, target(edge, g)));
vertex = python::object(PythonVertex(pg, target(edge, g)));
}
};
python::object GetTarget() const
{
CheckValid();
GraphInterface& gi = python::extract<GraphInterface&>(_g());
python::object v;
run_action<>()(_gi, bind<void>(get_target(), _1, ref(_gi), ref(_e),
ref(v)))();
run_action<>()(gi, bind<void>(get_target(), _1, ref(_g), ref(_e),
ref(v)))();
return v;
}
std::string GetString() const
{
CheckValid();
PythonVertex src = python::extract<PythonVertex>(GetSource());
PythonVertex tgt = python::extract<PythonVertex>(GetTarget());
return "(" + src.GetString() + "," + tgt.GetString() + ")";
......@@ -308,23 +324,26 @@ public:
size_t GetHash() const
{
CheckValid();
return hash<size_t>()(_gi._edge_index[_e]);
GraphInterface& gi = python::extract<GraphInterface&>(_g());
return hash<size_t>()(gi._edge_index[_e]);
}
bool operator==(const PythonEdge& other) const
{
CheckValid();
other.CheckValid();
return other._e == _e;
}
bool operator!=(const PythonEdge& other) const