Skip to content

Commit 53732b3

Browse files
committed
feat: more attribute handler functions
1 parent 57d40bd commit 53732b3

File tree

8 files changed

+292
-51
lines changed

8 files changed

+292
-51
lines changed

src/codegen/internal_lib.py.in

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@ igraph_strvector_clear = _lib.igraph_strvector_clear
259259
igraph_strvector_clear.restype = None
260260
igraph_strvector_clear.argtypes = [POINTER(igraph_strvector_t)]
261261

262+
igraph_strvector_push_back = _lib.igraph_strvector_push_back
263+
igraph_strvector_push_back.restype = handle_igraph_error_t
264+
igraph_strvector_push_back.argtypes = [POINTER(igraph_strvector_t), c_char_p]
265+
262266
igraph_strvector_resize = _lib.igraph_strvector_resize
263267
igraph_strvector_resize.restype = handle_igraph_error_t
264268
igraph_strvector_resize.argtypes = [POINTER(igraph_strvector_t), igraph_integer_t]
@@ -431,6 +435,14 @@ igraph_vs_all = _lib.igraph_vs_all
431435
igraph_vs_all.restype = handle_igraph_error_t
432436
igraph_vs_all.argtypes = [POINTER(igraph_vs_t)]
433437

438+
igraph_vs_as_vector = _lib.igraph_vs_as_vector
439+
igraph_vs_as_vector.restype = handle_igraph_error_t
440+
igraph_vs_as_vector.argtypes = [POINTER(igraph_t), igraph_vs_t, POINTER(igraph_vector_int_t)]
441+
442+
igraph_vs_type = _lib.igraph_vs_type
443+
igraph_vs_type.restype = c_int
444+
igraph_vs_type.argtypes = [POINTER(igraph_vs_t)]
445+
434446
igraph_vs_vector = _lib.igraph_vs_vector
435447
igraph_vs_vector.restype = handle_igraph_error_t
436448
igraph_vs_vector.argtypes = [POINTER(igraph_vs_t), POINTER(igraph_vector_int_t)]
@@ -457,6 +469,14 @@ igraph_es_all = _lib.igraph_es_all
457469
igraph_es_all.restype = handle_igraph_error_t
458470
igraph_es_all.argtypes = [POINTER(igraph_es_t)]
459471

472+
igraph_es_as_vector = _lib.igraph_es_as_vector
473+
igraph_es_as_vector.restype = handle_igraph_error_t
474+
igraph_es_as_vector.argtypes = [POINTER(igraph_t), igraph_es_t, POINTER(igraph_vector_int_t)]
475+
476+
igraph_es_type = _lib.igraph_es_type
477+
igraph_es_type.restype = c_int
478+
igraph_es_type.argtypes = [POINTER(igraph_es_t)]
479+
460480
igraph_es_vector = _lib.igraph_es_vector
461481
igraph_es_vector.restype = handle_igraph_error_t
462482
igraph_es_vector.argtypes = [POINTER(igraph_es_t), POINTER(igraph_vector_int_t)]

src/igraph_ctypes/_internal/attributes/handler.py

Lines changed: 134 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
1+
from __future__ import annotations
2+
13
from ctypes import pointer
24
from math import nan
3-
from typing import Any, Callable, Optional
5+
from typing import Any, Callable, Optional, TYPE_CHECKING
46

57
from igraph_ctypes._internal.conversion import igraph_vector_int_t_to_numpy_array_view
8+
from igraph_ctypes._internal.enums import AttributeElementType, AttributeType
69
from igraph_ctypes._internal.lib import (
710
igraph_error,
11+
igraph_es_as_vector,
812
igraph_vector_resize,
913
igraph_vector_set,
1014
igraph_vector_bool_resize,
1115
igraph_vector_bool_set,
1216
igraph_vector_int_clear,
17+
igraph_vector_int_push_back,
18+
igraph_vector_int_size,
19+
igraph_vs_as_vector,
1320
igraph_strvector_clear,
1421
igraph_strvector_resize,
22+
igraph_strvector_push_back,
1523
igraph_strvector_set,
1624
)
1725
from igraph_ctypes._internal.types import igraph_attribute_table_t
@@ -23,6 +31,11 @@
2331
detach_storage_from_graph,
2432
get_storage_from_graph,
2533
)
34+
from .utils import python_object_to_igraph_attribute_type
35+
from .value_list import AttributeValueList
36+
37+
if TYPE_CHECKING:
38+
from igraph_ctypes._internal.wrappers import _VectorInt
2639

2740
__all__ = (
2841
"AttributeHandlerBase",
@@ -72,8 +85,13 @@ class AttributeHandler(AttributeHandlerBase):
7285
as its storage backend.
7386
"""
7487

88+
_indices: _VectorInt
89+
7590
def init(self, graph, attr):
91+
from igraph_ctypes._internal.wrappers import _VectorInt
92+
7693
assign_storage_to_graph(graph, DictAttributeStorage())
94+
self._indices = _VectorInt.create(0)
7795

7896
def destroy(self, graph) -> None:
7997
storage = get_storage_from_graph(graph)
@@ -132,70 +150,141 @@ def combine_edges(self, graph, to, mapping, combinations):
132150
pass
133151

134152
def get_info(self, graph, gnames, gtypes, vnames, vtypes, enames, etypes):
135-
# TODO(ntamas): fill the names and the types
153+
storage = get_storage_from_graph(graph)
154+
136155
igraph_strvector_clear(gnames)
137156
igraph_vector_int_clear(gtypes)
157+
for name, value in storage.get_graph_attribute_map().items():
158+
igraph_strvector_push_back(gnames, name.encode("utf-8"))
159+
igraph_vector_int_push_back(
160+
gtypes, python_object_to_igraph_attribute_type(value)
161+
)
138162

139163
igraph_strvector_clear(vnames)
140164
igraph_vector_int_clear(vtypes)
165+
for name, value in storage.get_vertex_attribute_map().items():
166+
igraph_strvector_push_back(vnames, name.encode("utf-8"))
167+
igraph_vector_int_push_back(vtypes, value.type)
141168

142169
igraph_strvector_clear(enames)
143170
igraph_vector_int_clear(etypes)
171+
for name, value in storage.get_edge_attribute_map().items():
172+
igraph_strvector_push_back(enames, name.encode("utf-8"))
173+
igraph_vector_int_push_back(etypes, value.type)
174+
175+
def has_attr(self, graph, type: int, name: bytes) -> bool:
176+
storage = get_storage_from_graph(graph)
177+
name_str = name.decode("utf-8")
178+
179+
if type == AttributeElementType.GRAPH:
180+
map = storage.get_graph_attribute_map()
181+
elif type == AttributeElementType.VERTEX:
182+
map = storage.get_vertex_attribute_map()
183+
elif type == AttributeElementType.EDGE:
184+
map = storage.get_edge_attribute_map()
185+
else:
186+
return False
144187

145-
def has_attr(self, graph, type, name) -> bool:
146-
return False
188+
return name_str in map
147189

148190
def get_type(self, graph, type, elemtype, name):
149-
pass
191+
storage = get_storage_from_graph(graph)
192+
name_str = name.decode("utf-8")
150193

151-
def get_numeric_graph_attr(self, graph, name, value):
152-
vec = value.contents
153-
igraph_vector_resize(vec, 1)
154-
igraph_vector_set(
155-
vec,
156-
0,
157-
self._to_numeric(
158-
get_storage_from_graph(graph).get_graph_attribute_map()[name]
159-
),
160-
)
194+
if type == AttributeElementType.GRAPH:
195+
map = storage.get_graph_attribute_map()
196+
if name_str in map:
197+
return python_object_to_igraph_attribute_type(map[name_str])
198+
elif type == AttributeElementType.VERTEX:
199+
map = storage.get_vertex_attribute_map()
200+
if name_str in map:
201+
return map[name_str].type
202+
elif type == AttributeElementType.EDGE:
203+
map = storage.get_edge_attribute_map()
204+
else:
205+
return AttributeType.UNSPECIFIED
161206

162-
def get_string_graph_attr(self, graph, name, value):
163-
vec = value.contents
164-
igraph_strvector_resize(vec, 1)
165-
igraph_strvector_set(
166-
vec,
167-
0,
168-
self._to_bytes(
169-
get_storage_from_graph(graph).get_graph_attribute_map()[name]
170-
),
171-
)
207+
return map[name_str].type if name_str in map else AttributeType.UNSPECIFIED
172208

173-
def get_bool_graph_attr(self, graph, name, value):
174-
vec = value.contents
175-
igraph_vector_bool_resize(vec, 1)
176-
igraph_vector_bool_set(
177-
vec,
178-
0,
179-
bool(get_storage_from_graph(graph).get_graph_attribute_map()[name]),
180-
)
209+
def get_numeric_graph_attr(self, graph, name: bytes, value):
210+
map = get_storage_from_graph(graph).get_graph_attribute_map()
211+
igraph_vector_resize(value, 1)
212+
igraph_vector_set(value, 0, self._to_numeric(map[name.decode("utf-8")]))
181213

182-
def get_numeric_vertex_attr(self, graph, name, vs, value):
183-
pass
214+
def get_string_graph_attr(self, graph, name: bytes, value):
215+
map = get_storage_from_graph(graph).get_graph_attribute_map()
216+
igraph_strvector_resize(value, 1)
217+
igraph_strvector_set(value, 0, self._to_bytes(map[name.decode("utf-8")]))
184218

185-
def get_string_vertex_attr(self, graph, name, vs, value):
186-
pass
219+
def get_bool_graph_attr(self, graph, name: bytes, value):
220+
map = get_storage_from_graph(graph).get_graph_attribute_map()
221+
igraph_vector_bool_resize(value, 1)
222+
igraph_vector_bool_set(value, 0, bool(map[name.decode("utf-8")]))
187223

188-
def get_bool_vertex_attr(self, graph, name, vs, value):
189-
pass
224+
def get_numeric_vertex_attr(self, graph, name: bytes, vs, value):
225+
map = get_storage_from_graph(graph).get_vertex_attribute_map()
190226

191-
def get_numeric_edge_attr(self, graph, name, es, value):
192-
pass
227+
igraph_vs_as_vector(graph, vs, self._indices)
228+
values = self._get_values_by_index(map[name.decode("utf-8")], self._indices)
193229

194-
def get_string_edge_attr(self, graph, name, es, value):
195-
pass
230+
igraph_vector_resize(value, len(values))
231+
for i, v in enumerate(values):
232+
igraph_vector_set(value, i, self._to_numeric(v))
196233

197-
def get_bool_edge_attr(self, graph, name, es, value):
198-
pass
234+
def get_string_vertex_attr(self, graph, name: bytes, vs, value):
235+
map = get_storage_from_graph(graph).get_vertex_attribute_map()
236+
237+
igraph_vs_as_vector(graph, vs, self._indices)
238+
values = self._get_values_by_index(map[name.decode("utf-8")], self._indices)
239+
240+
igraph_strvector_resize(value, len(values))
241+
for i, v in enumerate(values):
242+
igraph_strvector_set(value, i, self._to_bytes(v))
243+
244+
def get_bool_vertex_attr(self, graph, name: bytes, vs, value):
245+
map = get_storage_from_graph(graph).get_vertex_attribute_map()
246+
247+
igraph_vs_as_vector(graph, vs, self._indices)
248+
values = self._get_values_by_index(map[name.decode("utf-8")], self._indices)
249+
250+
igraph_vector_bool_resize(value, len(values))
251+
for i, v in enumerate(values):
252+
igraph_vector_bool_set(value, i, bool(v))
253+
254+
def get_numeric_edge_attr(self, graph, name: bytes, es, value):
255+
map = get_storage_from_graph(graph).get_edge_attribute_map()
256+
257+
igraph_es_as_vector(graph, es, self._indices)
258+
values = self._get_values_by_index(map[name.decode("utf-8")], self._indices)
259+
260+
igraph_vector_resize(value, len(values))
261+
for i, v in enumerate(values):
262+
igraph_vector_set(value, i, self._to_numeric(v))
263+
264+
def get_string_edge_attr(self, graph, name: bytes, es, value):
265+
map = get_storage_from_graph(graph).get_edge_attribute_map()
266+
267+
igraph_es_as_vector(graph, es, self._indices)
268+
values = self._get_values_by_index(map[name.decode("utf-8")], self._indices)
269+
270+
igraph_strvector_resize(value, len(values))
271+
for i, v in enumerate(values):
272+
igraph_strvector_set(value, i, self._to_bytes(v))
273+
274+
def get_bool_edge_attr(self, graph, name: bytes, es, value):
275+
map = get_storage_from_graph(graph).get_edge_attribute_map()
276+
277+
igraph_es_as_vector(graph, es, self._indices)
278+
values = self._get_values_by_index(map[name.decode("utf-8")], self._indices)
279+
280+
igraph_vector_bool_resize(value, len(values))
281+
for i, v in enumerate(values):
282+
igraph_vector_bool_set(value, i, bool(v))
283+
284+
@staticmethod
285+
def _get_values_by_index(values: AttributeValueList, indices: _VectorInt):
286+
index_array = igraph_vector_int_t_to_numpy_array_view(indices)
287+
return values[index_array]
199288

200289
@staticmethod
201290
def _to_bytes(value: Any) -> bytes:

src/igraph_ctypes/_internal/lib.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@ def _load_libc():
259259
igraph_strvector_clear.restype = None
260260
igraph_strvector_clear.argtypes = [POINTER(igraph_strvector_t)]
261261

262+
igraph_strvector_push_back = _lib.igraph_strvector_push_back
263+
igraph_strvector_push_back.restype = handle_igraph_error_t
264+
igraph_strvector_push_back.argtypes = [POINTER(igraph_strvector_t), c_char_p]
265+
262266
igraph_strvector_resize = _lib.igraph_strvector_resize
263267
igraph_strvector_resize.restype = handle_igraph_error_t
264268
igraph_strvector_resize.argtypes = [POINTER(igraph_strvector_t), igraph_integer_t]
@@ -431,6 +435,14 @@ def _load_libc():
431435
igraph_vs_all.restype = handle_igraph_error_t
432436
igraph_vs_all.argtypes = [POINTER(igraph_vs_t)]
433437

438+
igraph_vs_as_vector = _lib.igraph_vs_as_vector
439+
igraph_vs_as_vector.restype = handle_igraph_error_t
440+
igraph_vs_as_vector.argtypes = [POINTER(igraph_t), igraph_vs_t, POINTER(igraph_vector_int_t)]
441+
442+
igraph_vs_type = _lib.igraph_vs_type
443+
igraph_vs_type.restype = c_int
444+
igraph_vs_type.argtypes = [POINTER(igraph_vs_t)]
445+
434446
igraph_vs_vector = _lib.igraph_vs_vector
435447
igraph_vs_vector.restype = handle_igraph_error_t
436448
igraph_vs_vector.argtypes = [POINTER(igraph_vs_t), POINTER(igraph_vector_int_t)]
@@ -457,6 +469,14 @@ def _load_libc():
457469
igraph_es_all.restype = handle_igraph_error_t
458470
igraph_es_all.argtypes = [POINTER(igraph_es_t)]
459471

472+
igraph_es_as_vector = _lib.igraph_es_as_vector
473+
igraph_es_as_vector.restype = handle_igraph_error_t
474+
igraph_es_as_vector.argtypes = [POINTER(igraph_t), igraph_es_t, POINTER(igraph_vector_int_t)]
475+
476+
igraph_es_type = _lib.igraph_es_type
477+
igraph_es_type.restype = c_int
478+
igraph_es_type.argtypes = [POINTER(igraph_es_t)]
479+
460480
igraph_es_vector = _lib.igraph_es_vector
461481
igraph_es_vector.restype = handle_igraph_error_t
462482
igraph_es_vector.argtypes = [POINTER(igraph_es_t), POINTER(igraph_vector_int_t)]

src/igraph_ctypes/_internal/types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,7 @@ class igraph_attribute_table_t(Structure):
538538
("combine_edges", TYPES["combine_edges"]),
539539
("get_info", TYPES["get_info"]),
540540
("has_attr", TYPES["has_attr"]),
541+
("get_type", TYPES["get_type"]),
541542
("get_numeric_graph_attr", TYPES["get_numeric_graph_attr"]),
542543
("get_string_graph_attr", TYPES["get_string_graph_attr"]),
543544
("get_bool_graph_attr", TYPES["get_bool_graph_attr"]),

src/igraph_ctypes/_internal/utils.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from ctypes import byref, cast, c_char_p, c_ubyte, POINTER, sizeof
22
from functools import wraps
3+
from traceback import print_exc
34
from typing import Callable, Union
45

56
from .errors import python_exception_to_igraph_error_t
@@ -42,6 +43,8 @@ def wrapped(*args, **kwds) -> int:
4243
try:
4344
func(*args, **kwds)
4445
except Exception as ex:
46+
print("Exception in callback invoked from igraph's C core:")
47+
print_exc()
4548
return python_exception_to_igraph_error_t(ex)
4649
else:
4750
return 0
@@ -65,14 +68,17 @@ def wrapped(*args, **kwds) -> int:
6568
func(*args, **kwds)
6669
return 0
6770
except Exception as ex:
68-
print(repr(ex))
71+
print("Exception in callback invoked from igraph's C core:")
72+
print_exc()
6973
code = python_exception_to_igraph_error_t(ex)
7074

7175
try:
7276
return handler(code)
73-
except Exception as ex:
74-
# TODO(ntamas): warn here!
75-
print(repr(ex))
77+
except Exception:
78+
print(
79+
"\nWhile handling the above exception, another exception occurred:"
80+
)
81+
print_exc()
7682
return code
7783

7884
return wrapped

tests/test_attribute_value_list.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,10 @@ def test_setitem_invalid_index(items: AVL):
330330
("to_delete", "expected"),
331331
[
332332
(2, [1, 2, 4, 5]),
333-
(slice(2, 4), [1, 2, 5]),
334333
(..., []),
334+
(slice(2, 0), [1, 2, 3, 4, 5]),
335+
(slice(2, 4), [1, 2, 5]),
336+
(slice(None), []),
335337
((), [1, 2, 3, 4, 5]),
336338
((0, 2, 4), [2, 4]),
337339
((False, False, False, False, False), [1, 2, 3, 4, 5]),
@@ -343,8 +345,10 @@ def test_setitem_invalid_index(items: AVL):
343345
],
344346
ids=(
345347
"single_index",
346-
"slice",
347348
"ellipsis",
349+
"empty_slice",
350+
"slice",
351+
"whole_slice",
348352
"empty_tuple",
349353
"tuple",
350354
"empty_bool_mask",

0 commit comments

Comments
 (0)