1- from ctypes import c_char_p , c_int , c_void_p , CFUNCTYPE , POINTER , Structure
1+ from ctypes import (
2+ c_char_p ,
3+ c_int ,
4+ c_void_p ,
5+ pointer ,
6+ py_object ,
7+ CFUNCTYPE ,
8+ POINTER ,
9+ Structure ,
10+ )
11+ from dataclasses import dataclass , field , replace
12+ from typing import Any , Callable , Dict , Optional
213
14+ from .refcount import incref , decref , refcount
315from .types import (
416 igraph_bool_t ,
517 igraph_error_t ,
1426 igraph_vector_t ,
1527 igraph_vs_t ,
1628)
29+ from .utils import protect
30+
31+ __all__ = ("AttributeHandlerBase" , "DictAttributeHandler" )
1732
1833
1934p_igraph_t = POINTER (igraph_t )
@@ -94,26 +109,32 @@ class igraph_attribute_table_t(Structure):
94109 "get_type" : CFUNCTYPE (
95110 igraph_error_t , p_igraph_t , POINTER (c_int ), c_int , c_char_p
96111 ),
97- "get_numeric_graph_attr" : CFUNCTYPE (p_igraph_t , c_char_p , p_vector_t ),
98- "get_string_graph_attr" : CFUNCTYPE (p_igraph_t , c_char_p , p_strvector_t ),
99- "get_bool_graph_attr" : CFUNCTYPE (p_igraph_t , c_char_p , p_vector_bool_t ),
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+ ),
100121 "get_numeric_vertex_attr" : CFUNCTYPE (
101- p_igraph_t , c_char_p , igraph_vs_t , p_vector_t
122+ igraph_error_t , p_igraph_t , c_char_p , igraph_vs_t , p_vector_t
102123 ),
103124 "get_string_vertex_attr" : CFUNCTYPE (
104- p_igraph_t , c_char_p , igraph_vs_t , p_strvector_t
125+ igraph_error_t , p_igraph_t , c_char_p , igraph_vs_t , p_strvector_t
105126 ),
106127 "get_bool_vertex_attr" : CFUNCTYPE (
107- p_igraph_t , c_char_p , igraph_vs_t , p_vector_bool_t
128+ igraph_error_t , p_igraph_t , c_char_p , igraph_vs_t , p_vector_bool_t
108129 ),
109130 "get_numeric_edge_attr" : CFUNCTYPE (
110- p_igraph_t , c_char_p , igraph_es_t , p_vector_t
131+ igraph_error_t , p_igraph_t , c_char_p , igraph_es_t , p_vector_t
111132 ),
112133 "get_string_edge_attr" : CFUNCTYPE (
113- p_igraph_t , c_char_p , igraph_es_t , p_strvector_t
134+ igraph_error_t , p_igraph_t , c_char_p , igraph_es_t , p_strvector_t
114135 ),
115136 "get_bool_edge_attr" : CFUNCTYPE (
116- p_igraph_t , c_char_p , igraph_es_t , p_vector_bool_t
137+ igraph_error_t , p_igraph_t , c_char_p , igraph_es_t , p_vector_bool_t
117138 ),
118139 }
119140
@@ -139,3 +160,186 @@ class igraph_attribute_table_t(Structure):
139160 ("get_string_edge_attr" , TYPES ["get_string_edge_attr" ]),
140161 ("get_bool_edge_attr" , TYPES ["get_bool_edge_attr" ]),
141162 ]
163+
164+
165+ ################################################################################
166+
167+
168+ class AttributeHandlerBase :
169+ """Base class for igraph attribute handlers."""
170+
171+ _table : Optional [igraph_attribute_table_t ] = None
172+ _table_ptr = None
173+
174+ def _get_attribute_handler_functions (self ) -> Dict [str , Callable ]:
175+ """Returns an ``igraph_attribute_table_t`` instance that can be used
176+ to register this attribute handler in the core igraph library.
177+ """
178+ return {
179+ key : igraph_attribute_table_t .TYPES [key ](
180+ protect (getattr (self , key , self ._nop ))
181+ )
182+ for key in igraph_attribute_table_t .TYPES .keys ()
183+ }
184+
185+ @property
186+ def _as_parameter_ (self ):
187+ if self ._table_ptr is None :
188+ self ._table = igraph_attribute_table_t (
189+ ** self ._get_attribute_handler_functions ()
190+ )
191+ self ._table_ptr = pointer (self ._table )
192+ return self ._table_ptr
193+
194+ @staticmethod
195+ def _nop ():
196+ pass
197+
198+
199+ @dataclass (frozen = True )
200+ class _DictAttributeStorage :
201+ """Dictionary-based storage area for the graph, vertex and edge attributes
202+ of a graph.
203+ """
204+
205+ graph_attributes : Dict [str , Any ] = field (default_factory = dict )
206+ vertex_attributes : Dict [str , Any ] = field (default_factory = dict )
207+ edge_attributes : Dict [str , Any ] = field (default_factory = dict )
208+
209+ def clear (self ) -> None :
210+ """Clears the storage area, removing all attributes from the
211+ attribute dictionaries.
212+ """
213+ self .graph_attributes .clear ()
214+ self .vertex_attributes .clear ()
215+ self .edge_attributes .clear ()
216+
217+ def copy (
218+ self ,
219+ copy_graph_attributes : bool = True ,
220+ copy_vertex_attributes : bool = True ,
221+ copy_edge_attributes : bool = True ,
222+ ):
223+ """Creates a shallow copy of the storage area."""
224+ return self .__class__ (
225+ self .graph_attributes .copy () if copy_graph_attributes else {},
226+ self .vertex_attributes .copy () if copy_vertex_attributes else {},
227+ self .edge_attributes .copy () if copy_edge_attributes else {},
228+ )
229+
230+
231+ _MISSING = object ()
232+
233+
234+ def _assign_storage_to_graph (graph , storage : Any = _MISSING ):
235+ """Assigns an attribute storage object to a graph, taking care of
236+ increasing or decreasing the reference count of the storage object if needed.
237+ """
238+ try :
239+ old_storage = graph .contents .attr
240+ except ValueError :
241+ # No storage yet, this is OK
242+ old_storage = _MISSING
243+
244+ if old_storage is storage :
245+ # Nothing to do
246+ return
247+
248+ if old_storage is not _MISSING :
249+ decref (old_storage )
250+
251+ if storage is not _MISSING :
252+ graph .contents .attr = py_object (incref (storage ))
253+ else :
254+ graph .contents .attr = py_object ()
255+
256+
257+ def _detach_storage_from_graph (graph ):
258+ return _assign_storage_to_graph (graph , _MISSING )
259+
260+
261+ class DictAttributeHandler (AttributeHandlerBase ):
262+ """Attribute handler implementation that stores graph, vertex and edge
263+ attributes in dictionaries.
264+ """
265+
266+ def init (self , graph , attr ):
267+ _assign_storage_to_graph (graph , _DictAttributeStorage ())
268+
269+ def destroy (self , graph ) -> None :
270+ storage : _DictAttributeStorage = graph .contents .attr
271+ storage .clear ()
272+ _detach_storage_from_graph (graph )
273+
274+ def copy (
275+ self ,
276+ to ,
277+ graph ,
278+ copy_graph_attributes : bool ,
279+ copy_vertex_attributes : bool ,
280+ copy_edge_attributes : bool ,
281+ ):
282+ try :
283+ storage = incref (
284+ graph .contents .attr .copy (
285+ copy_graph_attributes , copy_vertex_attributes , copy_edge_attributes
286+ )
287+ )
288+ to .contents .attr = py_object (storage )
289+ except Exception as ex :
290+ print (repr (ex ))
291+ raise
292+
293+ def add_vertices (self , graph , n : int , attr ) -> None :
294+ pass
295+
296+ def permute_vertices (self , graph , to , mapping ):
297+ pass
298+
299+ def combine_vertices (self , graph , to , mapping , combinations ):
300+ pass
301+
302+ def add_edges (self , graph , edges , attr ) -> None :
303+ pass
304+
305+ def permute_edges (self , graph , to , mapping ):
306+ pass
307+
308+ def combine_edges (self , graph , to , mapping , combinations ):
309+ pass
310+
311+ def get_info (self , graph , gnames , gtypes , vnames , vtypes , enames , etypes ):
312+ pass
313+
314+ def has_attr (self , graph , type , name ) -> bool :
315+ return False
316+
317+ def get_type (self , graph , type , elemtype , name ):
318+ pass
319+
320+ def get_numeric_graph_attr (self , graph , name , value ):
321+ pass
322+
323+ def get_string_graph_attr (self , graph , name , value ):
324+ pass
325+
326+ def get_boolean_graph_attr (self , graph , name , value ):
327+ pass
328+
329+ def get_numeric_vertex_attr (self , graph , name , vs , value ):
330+ pass
331+
332+ def get_string_vertex_attr (self , graph , name , vs , value ):
333+ pass
334+
335+ def get_boolean_vertex_attr (self , graph , name , vs , value ):
336+ pass
337+
338+ def get_numeric_edge_attr (self , graph , name , es , value ):
339+ pass
340+
341+ def get_string_edge_attr (self , graph , name , es , value ):
342+ pass
343+
344+ def get_boolean_edge_attr (self , graph , name , es , value ):
345+ pass
0 commit comments