1+ from abc import ABC , abstractmethod
12from ctypes import (
2- c_char_p ,
3- c_int ,
4- c_void_p ,
53 pointer ,
64 py_object ,
7- CFUNCTYPE ,
8- POINTER ,
9- Structure ,
105)
116from dataclasses import dataclass , field
127from typing import Any , Callable , Dict , Optional
138
9+ from .lib import igraph_error , igraph_vector_ptr_size
1410from .refcount import incref , decref
15- from .types import (
16- igraph_bool_t ,
17- igraph_error_t ,
18- igraph_es_t ,
19- igraph_integer_t ,
20- igraph_strvector_t ,
21- igraph_t ,
22- igraph_vector_bool_t ,
23- igraph_vector_int_t ,
24- igraph_vector_int_list_t ,
25- igraph_vector_ptr_t ,
26- igraph_vector_t ,
27- igraph_vs_t ,
28- )
29- from .utils import nop , protect
30-
31- __all__ = ("AttributeHandlerBase" , "DictAttributeHandler" )
32-
33-
34- p_igraph_t = POINTER (igraph_t )
35- p_strvector_t = POINTER (igraph_strvector_t )
36- p_vector_t = POINTER (igraph_vector_t )
37- p_vector_bool_t = POINTER (igraph_vector_bool_t )
38- p_vector_int_t = POINTER (igraph_vector_int_t )
39- p_vector_int_list_t = POINTER (igraph_vector_int_list_t )
40- p_vector_ptr_t = POINTER (igraph_vector_ptr_t )
41-
42-
43- class igraph_attribute_combination_t (Structure ):
44- """ctypes representation of ``igraph_attribute_combination_t``"""
45-
46- _fields_ = [("list" , igraph_vector_ptr_t )]
47-
48-
49- class igraph_attribute_combination_record_t (Structure ):
50- """ctypes representation of ``igraph_attribute_combination_record_t``"""
51-
52- _fields_ = [("name" , c_char_p ), ("type" , c_int ), ("func" , CFUNCTYPE (c_void_p ))]
53-
54-
55- p_attribute_combination_t = POINTER (igraph_attribute_combination_t )
56-
57-
58- class igraph_attribute_table_t (Structure ):
59- """ctypes representation of ``igraph_attribute_table_t``"""
60-
61- TYPES = {
62- "init" : CFUNCTYPE (igraph_error_t , p_igraph_t , p_vector_ptr_t ),
63- "destroy" : CFUNCTYPE (None , p_igraph_t ),
64- "copy" : CFUNCTYPE (
65- igraph_error_t ,
66- p_igraph_t ,
67- p_igraph_t ,
68- igraph_bool_t ,
69- igraph_bool_t ,
70- igraph_bool_t ,
71- ),
72- "add_vertices" : CFUNCTYPE (
73- igraph_error_t , p_igraph_t , igraph_integer_t , p_vector_ptr_t
74- ),
75- "permute_vertices" : CFUNCTYPE (
76- igraph_error_t , p_igraph_t , p_igraph_t , p_vector_int_t
77- ),
78- "combine_vertices" : CFUNCTYPE (
79- igraph_error_t ,
80- p_igraph_t ,
81- p_igraph_t ,
82- p_vector_int_list_t ,
83- p_attribute_combination_t ,
84- ),
85- "add_edges" : CFUNCTYPE (
86- igraph_error_t , p_igraph_t , p_vector_int_t , p_vector_ptr_t
87- ),
88- "permute_edges" : CFUNCTYPE (
89- igraph_error_t , p_igraph_t , p_igraph_t , p_vector_int_t
90- ),
91- "combine_edges" : CFUNCTYPE (
92- igraph_error_t ,
93- p_igraph_t ,
94- p_igraph_t ,
95- p_vector_int_list_t ,
96- p_attribute_combination_t ,
97- ),
98- "get_info" : CFUNCTYPE (
99- igraph_error_t ,
100- p_igraph_t ,
101- p_strvector_t ,
102- p_vector_int_t ,
103- p_strvector_t ,
104- p_vector_int_t ,
105- p_strvector_t ,
106- p_vector_int_t ,
107- ),
108- "has_attr" : CFUNCTYPE (igraph_bool_t , p_igraph_t , c_int , c_char_p ),
109- "get_type" : CFUNCTYPE (
110- igraph_error_t , p_igraph_t , POINTER (c_int ), c_int , c_char_p
111- ),
112- "get_numeric_graph_attr" : CFUNCTYPE (
113- igraph_error_t , p_igraph_t , c_char_p , p_vector_t
114- ),
115- "get_string_graph_attr" : CFUNCTYPE (
116- igraph_error_t , p_igraph_t , c_char_p , p_strvector_t
117- ),
118- "get_bool_graph_attr" : CFUNCTYPE (
119- igraph_error_t , p_igraph_t , c_char_p , p_vector_bool_t
120- ),
121- "get_numeric_vertex_attr" : CFUNCTYPE (
122- igraph_error_t , p_igraph_t , c_char_p , igraph_vs_t , p_vector_t
123- ),
124- "get_string_vertex_attr" : CFUNCTYPE (
125- igraph_error_t , p_igraph_t , c_char_p , igraph_vs_t , p_strvector_t
126- ),
127- "get_bool_vertex_attr" : CFUNCTYPE (
128- igraph_error_t , p_igraph_t , c_char_p , igraph_vs_t , p_vector_bool_t
129- ),
130- "get_numeric_edge_attr" : CFUNCTYPE (
131- igraph_error_t , p_igraph_t , c_char_p , igraph_es_t , p_vector_t
132- ),
133- "get_string_edge_attr" : CFUNCTYPE (
134- igraph_error_t , p_igraph_t , c_char_p , igraph_es_t , p_strvector_t
135- ),
136- "get_bool_edge_attr" : CFUNCTYPE (
137- igraph_error_t , p_igraph_t , c_char_p , igraph_es_t , p_vector_bool_t
138- ),
139- }
140-
141- _fields_ = [
142- ("init" , TYPES ["init" ]),
143- ("destroy" , TYPES ["destroy" ]),
144- ("copy" , TYPES ["copy" ]),
145- ("add_vertices" , TYPES ["add_vertices" ]),
146- ("permute_vertices" , TYPES ["permute_vertices" ]),
147- ("combine_vertices" , TYPES ["combine_vertices" ]),
148- ("add_edges" , TYPES ["add_edges" ]),
149- ("permute_edges" , TYPES ["permute_edges" ]),
150- ("combine_edges" , TYPES ["combine_edges" ]),
151- ("get_info" , TYPES ["get_info" ]),
152- ("has_attr" , TYPES ["has_attr" ]),
153- ("get_numeric_graph_attr" , TYPES ["get_numeric_graph_attr" ]),
154- ("get_string_graph_attr" , TYPES ["get_string_graph_attr" ]),
155- ("get_bool_graph_attr" , TYPES ["get_bool_graph_attr" ]),
156- ("get_numeric_vertex_attr" , TYPES ["get_numeric_vertex_attr" ]),
157- ("get_string_vertex_attr" , TYPES ["get_string_vertex_attr" ]),
158- ("get_bool_vertex_attr" , TYPES ["get_bool_vertex_attr" ]),
159- ("get_numeric_edge_attr" , TYPES ["get_numeric_edge_attr" ]),
160- ("get_string_edge_attr" , TYPES ["get_string_edge_attr" ]),
161- ("get_bool_edge_attr" , TYPES ["get_bool_edge_attr" ]),
162- ]
11+ from .types import igraph_attribute_table_t
12+ from .utils import nop , protect_with
13+
14+ __all__ = ("AttributeHandlerBase" , "AttributeHandler" , "AttributeStorage" )
16315
16416
16517################################################################################
16618
16719
20+ def _trigger_error (error : int ) -> int :
21+ return int (
22+ igraph_error (
23+ b"Attribute handler triggered an error" ,
24+ b"<py-attribute-handler>" ,
25+ 1 ,
26+ int (error ),
27+ )
28+ )
29+
30+
16831class AttributeHandlerBase :
16932 """Base class for igraph attribute handlers."""
17033
@@ -175,6 +38,7 @@ def _get_attribute_handler_functions(self) -> Dict[str, Callable]:
17538 """Returns an ``igraph_attribute_table_t`` instance that can be used
17639 to register this attribute handler in the core igraph library.
17740 """
41+ protect = protect_with (_trigger_error )
17842 return {
17943 key : igraph_attribute_table_t .TYPES [key ](protect (getattr (self , key , nop )))
18044 for key in igraph_attribute_table_t .TYPES .keys ()
@@ -190,8 +54,35 @@ def _as_parameter_(self):
19054 return self ._table_ptr
19155
19256
57+ class AttributeStorage (ABC ):
58+ """Interface specification for objects that store graph, vertex and edge
59+ attributes.
60+ """
61+
62+ @abstractmethod
63+ def add_vertices (self , n : int ) -> None :
64+ """Notifies the attribute storage object that the given number of
65+ new vertices were added to the graph.
66+ """
67+ raise NotImplementedError
68+
69+ @abstractmethod
70+ def clear (self ):
71+ """Clears the storage area, removing all attributes."""
72+ raise NotImplementedError
73+
74+ @abstractmethod
75+ def copy (
76+ self ,
77+ copy_graph_attributes : bool = True ,
78+ copy_vertex_attributes : bool = True ,
79+ copy_edge_attributes : bool = True ,
80+ ):
81+ raise NotImplementedError
82+
83+
19384@dataclass (frozen = True )
194- class _DictAttributeStorage :
85+ class DictAttributeStorage ( AttributeStorage ) :
19586 """Dictionary-based storage area for the graph, vertex and edge attributes
19687 of a graph.
19788 """
@@ -200,6 +91,9 @@ class _DictAttributeStorage:
20091 vertex_attributes : Dict [str , Any ] = field (default_factory = dict )
20192 edge_attributes : Dict [str , Any ] = field (default_factory = dict )
20293
94+ def add_vertices (self , graph , n : int ) -> None :
95+ print ("Added" , n , "vertices" )
96+
20397 def clear (self ) -> None :
20498 """Clears the storage area, removing all attributes from the
20599 attribute dictionaries.
@@ -222,47 +116,50 @@ def copy(
222116 )
223117
224118
225- _MISSING = object ()
226-
227-
228- def _assign_storage_to_graph (graph , storage : Any = _MISSING ):
119+ def _assign_storage_to_graph (graph , storage : Optional [AttributeStorage ] = None ) -> None :
229120 """Assigns an attribute storage object to a graph, taking care of
230121 increasing or decreasing the reference count of the storage object if needed.
231122 """
232123 try :
233124 old_storage = graph .contents .attr
234125 except ValueError :
235126 # No storage yet, this is OK
236- old_storage = _MISSING
127+ old_storage = None
237128
238129 if old_storage is storage :
239130 # Nothing to do
240131 return
241132
242- if old_storage is not _MISSING :
133+ if old_storage is not None :
243134 decref (old_storage )
244135
245- if storage is not _MISSING :
136+ if storage is not None :
246137 graph .contents .attr = py_object (incref (storage ))
247138 else :
248139 graph .contents .attr = py_object ()
249140
250141
251- def _detach_storage_from_graph (graph ):
252- return _assign_storage_to_graph (graph , _MISSING )
142+ def _get_storage_from_graph (graph ) -> AttributeStorage :
143+ return graph .contents .attr
144+
253145
146+ def _detach_storage_from_graph (graph ) -> None :
147+ return _assign_storage_to_graph (graph , None )
254148
255- class DictAttributeHandler (AttributeHandlerBase ):
256- """Attribute handler implementation that stores graph, vertex and edge
257- attributes in dictionaries.
149+
150+ class AttributeHandler (AttributeHandlerBase ):
151+ """Attribute handler implementation that uses a DictAttributeStorage_
152+ as its storage backend.
258153 """
259154
260155 def init (self , graph , attr ):
261- _assign_storage_to_graph (graph , _DictAttributeStorage ())
156+ _assign_storage_to_graph (graph , DictAttributeStorage ())
262157
263158 def destroy (self , graph ) -> None :
264- storage : _DictAttributeStorage = graph .contents .attr
265- storage .clear ()
159+ storage = _get_storage_from_graph (graph )
160+ if storage :
161+ storage .clear ()
162+
266163 _detach_storage_from_graph (graph )
267164
268165 def copy (
@@ -273,19 +170,22 @@ def copy(
273170 copy_vertex_attributes : bool ,
274171 copy_edge_attributes : bool ,
275172 ):
276- try :
277- storage = incref (
278- graph .contents .attr .copy (
279- copy_graph_attributes , copy_vertex_attributes , copy_edge_attributes
280- )
281- )
282- to .contents .attr = py_object (storage )
283- except Exception as ex :
284- print (repr (ex ))
285- raise
173+ storage = _get_storage_from_graph (graph )
174+ new_storage = storage .copy (
175+ copy_graph_attributes , copy_vertex_attributes , copy_edge_attributes
176+ )
177+ _assign_storage_to_graph (to , new_storage )
286178
287179 def add_vertices (self , graph , n : int , attr ) -> None :
288- pass
180+ # attr will only ever be NULL here so raise an error if it is not
181+ if attr :
182+ raise RuntimeError (
183+ "add_vertices() attribute handler called with non-null attr; "
184+ "this is most likely a bug"
185+ )
186+
187+ # Extend the existing attribute containers
188+ _get_storage_from_graph (graph ).add_vertices (graph , n )
289189
290190 def permute_vertices (self , graph , to , mapping ):
291191 pass
0 commit comments