Skip to content

Commit 1bc28a4

Browse files
aMahannahkernbach
andauthored
GA-163 | test_multigraph & test_multidigraph (#42)
* GA-163 | initial commit will fail * unlock adbnx * fix: `incoming_graph_data` * fix: incoming_graph_data * fix: off-by-one IDs * checkpoint * checkpoint: `BaseGraphTester` is passing * checkpoint: BaseGraphAttrTester * cleanup: `aql_fetch_data`, `aql_fetch_data_edge` * use pytest skip for failing tests * checkpoint: optimize `__iter__` * checkpoint: run `test_graph` * add comment * checkpoint * attempt: slleep * fix: lint * cleanup: getitem * cleanup: copy * attempt: shorten sleep * fix: `__set_adj_elements` * fix: mypy * attempt: decrease sleep * GA-163 | `test_digraph` * checkpoint lots of failures... * fix: set `self.Graph` * add type ignore * fix: graph name * fix: graph name * adjust assertions to exclude _rev, set `use_experimental_views` * Revert "adjust assertions to exclude _rev, set `use_experimental_views`" This reverts commit b805419. * fix: `_rev`, `use_experimental_views` * set `use_experimental_views` * fix: lint * new: `nbunch_iter` override * set experimental views to false * set experimental views to false * cleanup * GA-163 | `test_multigraph` checkpoint * fix lint * fix: `function.py` * cleanup: `graph`, `digraph` * fix: `test_data_input` * attempt: wait for CircleCI * fix: nx graph * remove sleep * new: `override` suffix * add override * enable more tests * fix: lint * checkpoint tests are still failing * checkpoint: 2 remaining test failures * fix: lint * checkpoint: one last failing test tried to debug this. no answer yet.. * remove: `logger_debug`, fix lint * lint * fix: `test_multigraph` * cleanup, add missing test * new: `test_non_multigraph_input_a` * add comments * GA-163 | `test_multidigraph` (#45) * checkpoint: `test_multidigraph` * checkpoint: 1 failing test for each file: `test_digraph`, `test_multigraph`, `test_multidigraph` * fix: `test_to_undirected_reciprocal` * remove unused block * fix: `write_async` False --------- Co-authored-by: hkernbach <[email protected]>
1 parent 04c42c9 commit 1bc28a4

File tree

13 files changed

+1998
-251
lines changed

13 files changed

+1998
-251
lines changed

nx_arangodb/classes/dict/adj.py

Lines changed: 69 additions & 91 deletions
Large diffs are not rendered by default.

nx_arangodb/classes/dict/graph.py

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
json_serializable,
1717
key_is_not_reserved,
1818
key_is_string,
19-
logger_debug,
2019
)
2120

2221
#############
@@ -80,7 +79,6 @@ class GraphDict(UserDict[str, Any]):
8079
:type graph_name: str
8180
"""
8281

83-
@logger_debug
8482
def __init__(self, db: StandardDatabase, graph: Graph, *args: Any, **kwargs: Any):
8583
super().__init__(*args, **kwargs)
8684
self.data: dict[str, Any] = {}
@@ -111,7 +109,6 @@ def __process_graph_dict_value(self, key: str, value: Any) -> Any:
111109
return graph_attr_dict
112110

113111
@key_is_string
114-
@logger_debug
115112
def __contains__(self, key: str) -> bool:
116113
"""'foo' in G.graph"""
117114
if key in self.data:
@@ -120,7 +117,6 @@ def __contains__(self, key: str) -> bool:
120117
return aql_doc_has_key(self.db, self.graph_id, key)
121118

122119
@key_is_string
123-
@logger_debug
124120
def __getitem__(self, key: str) -> Any:
125121
"""G.graph['foo']"""
126122

@@ -139,7 +135,6 @@ def __getitem__(self, key: str) -> Any:
139135

140136
@key_is_string
141137
@key_is_not_reserved
142-
@logger_debug
143138
def __setitem__(self, key: str, value: Any) -> None:
144139
"""G.graph['foo'] = 'bar'"""
145140
if value is None:
@@ -152,15 +147,13 @@ def __setitem__(self, key: str, value: Any) -> None:
152147

153148
@key_is_string
154149
@key_is_not_reserved
155-
@logger_debug
156150
def __delitem__(self, key: str) -> None:
157151
"""del G.graph['foo']"""
158152
self.data.pop(key, None)
159153
doc_update(self.db, self.graph_id, {key: None})
160154

161155
# @values_are_json_serializable # TODO?
162-
@logger_debug
163-
def update(self, attrs: Any) -> None:
156+
def update(self, attrs: Any) -> None: # type: ignore
164157
"""G.graph.update({'foo': 'bar'})"""
165158

166159
if not attrs:
@@ -173,7 +166,6 @@ def update(self, attrs: Any) -> None:
173166
self.data.update(graph_attr_dict_data)
174167
doc_update(self.db, self.graph_id, attrs)
175168

176-
@logger_debug
177169
def clear(self) -> None:
178170
"""G.graph.clear()"""
179171
self.data.clear()
@@ -194,7 +186,6 @@ class GraphAttrDict(UserDict[str, Any]):
194186
:type graph_id: str
195187
"""
196188

197-
@logger_debug
198189
def __init__(
199190
self,
200191
db: StandardDatabase,
@@ -219,7 +210,6 @@ def clear(self) -> None:
219210
raise NotImplementedError("Cannot clear GraphAttrDict")
220211

221212
@key_is_string
222-
@logger_debug
223213
def __contains__(self, key: str) -> bool:
224214
"""'bar' in G.graph['foo']"""
225215
if key in self.data:
@@ -228,7 +218,6 @@ def __contains__(self, key: str) -> bool:
228218
return aql_doc_has_key(self.db, self.graph.name, key, self.parent_keys)
229219

230220
@key_is_string
231-
@logger_debug
232221
def __getitem__(self, key: str) -> Any:
233222
"""G.graph['foo']['bar']"""
234223

@@ -246,7 +235,6 @@ def __getitem__(self, key: str) -> Any:
246235
return graph_attr_dict_value
247236

248237
@key_is_string
249-
@logger_debug
250238
def __setitem__(self, key, value):
251239
"""
252240
G.graph['foo'] = 'bar'
@@ -263,15 +251,13 @@ def __setitem__(self, key, value):
263251
doc_update(self.db, self.graph_id, update_dict)
264252

265253
@key_is_string
266-
@logger_debug
267254
def __delitem__(self, key):
268255
"""del G.graph['foo']['bar']"""
269256
self.data.pop(key, None)
270257
update_dict = get_update_dict(self.parent_keys, {key: None})
271258
doc_update(self.db, self.graph_id, update_dict)
272259

273-
@logger_debug
274-
def update(self, attrs: Any) -> None:
260+
def update(self, attrs: Any) -> None: # type: ignore
275261
"""G.graph['foo'].update({'bar': 'baz'})"""
276262
if not attrs:
277263
return

nx_arangodb/classes/dict/node.py

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
key_is_string,
2929
keys_are_not_reserved,
3030
keys_are_strings,
31-
logger_debug,
3231
separate_nodes_by_collections,
3332
upsert_collection_documents,
3433
vertex_get,
@@ -97,7 +96,6 @@ class NodeAttrDict(UserDict[str, Any]):
9796
:type graph: Graph
9897
"""
9998

100-
@logger_debug
10199
def __init__(self, db: StandardDatabase, graph: Graph, *args: Any, **kwargs: Any):
102100
super().__init__(*args, **kwargs)
103101
self.data: dict[str, Any] = {}
@@ -116,11 +114,9 @@ def clear(self) -> None:
116114
raise NotImplementedError("Cannot clear NodeAttrDict")
117115

118116
def copy(self) -> Any:
119-
# TODO: REVISIT THIS
120117
return self.data.copy()
121118

122119
@key_is_string
123-
@logger_debug
124120
def __contains__(self, key: str) -> bool:
125121
"""'foo' in G._node['node/1']"""
126122
if key in self.data:
@@ -131,7 +127,6 @@ def __contains__(self, key: str) -> bool:
131127
return result
132128

133129
@key_is_string
134-
@logger_debug
135130
def __getitem__(self, key: str) -> Any:
136131
"""G._node['node/1']['foo']"""
137132
if key in self.data:
@@ -151,7 +146,6 @@ def __getitem__(self, key: str) -> Any:
151146
@key_is_string
152147
@key_is_not_reserved
153148
# @value_is_json_serializable # TODO?
154-
@logger_debug
155149
def __setitem__(self, key: str, value: Any) -> None:
156150
"""
157151
G._node['node/1']['foo'] = 'bar'
@@ -170,7 +164,6 @@ def __setitem__(self, key: str, value: Any) -> None:
170164

171165
@key_is_string
172166
@key_is_not_reserved
173-
@logger_debug
174167
def __delitem__(self, key: str) -> None:
175168
"""del G._node['node/1']['foo']"""
176169
assert self.node_id
@@ -181,7 +174,6 @@ def __delitem__(self, key: str) -> None:
181174
@keys_are_strings
182175
@keys_are_not_reserved
183176
# @values_are_json_serializable # TODO?
184-
@logger_debug
185177
def update(self, attrs: Any) -> None:
186178
"""G._node['node/1'].update({'foo': 'bar'})"""
187179
if not attrs:
@@ -213,7 +205,6 @@ class NodeDict(UserDict[str, NodeAttrDict]):
213205
:type default_node_type: str
214206
"""
215207

216-
@logger_debug
217208
def __init__(
218209
self,
219210
db: StandardDatabase,
@@ -250,7 +241,6 @@ def __str__(self) -> str:
250241
return self.__repr__()
251242

252243
@key_is_string
253-
@logger_debug
254244
def __contains__(self, key: str) -> bool:
255245
"""'node/1' in G._node"""
256246
node_id = get_node_id(key, self.default_node_type)
@@ -270,7 +260,6 @@ def __contains__(self, key: str) -> bool:
270260
return False
271261

272262
@key_is_string
273-
@logger_debug
274263
def __getitem__(self, key: str) -> NodeAttrDict:
275264
"""G._node['node/1']"""
276265
node_id = get_node_id(key, self.default_node_type)
@@ -290,7 +279,6 @@ def __getitem__(self, key: str) -> NodeAttrDict:
290279
raise KeyError(key)
291280

292281
@key_is_string
293-
@logger_debug
294282
def __setitem__(self, key: str, value: NodeAttrDict) -> None:
295283
"""G._node['node/1'] = {'foo': 'bar'}
296284
@@ -308,7 +296,6 @@ def __setitem__(self, key: str, value: NodeAttrDict) -> None:
308296
self.data[node_id] = node_attr_dict
309297

310298
@key_is_string
311-
@logger_debug
312299
def __delitem__(self, key: str) -> None:
313300
"""del g._node['node/1']"""
314301
node_id = get_node_id(key, self.default_node_type)
@@ -336,7 +323,6 @@ def __delitem__(self, key: str) -> None:
336323

337324
self.data.pop(node_id, None)
338325

339-
@logger_debug
340326
def __len__(self) -> int:
341327
"""len(g._node)"""
342328
return sum(
@@ -346,15 +332,13 @@ def __len__(self) -> int:
346332
]
347333
)
348334

349-
@logger_debug
350335
def __iter__(self) -> Iterator[str]:
351336
"""for k in g._node"""
352337
if not (self.FETCHED_ALL_IDS or self.FETCHED_ALL_DATA):
353338
self._fetch_all()
354339

355340
yield from self.data.keys()
356341

357-
@logger_debug
358342
def keys(self) -> Any:
359343
"""g._node.keys()"""
360344
if self.FETCHED_ALL_IDS:
@@ -368,15 +352,20 @@ def keys(self) -> Any:
368352
self.data[node_id] = empty_node_attr_dict
369353
yield node_id
370354

371-
@logger_debug
372355
def clear(self) -> None:
373356
"""g._node.clear()"""
374357
self.data.clear()
375358
self.FETCHED_ALL_DATA = False
376359
self.FETCHED_ALL_IDS = False
377360

361+
def copy(self) -> Any:
362+
"""g._node.copy()"""
363+
if not self.FETCHED_ALL_DATA:
364+
self._fetch_all()
365+
366+
return {key: value.copy() for key, value in self.data.items()}
367+
378368
@keys_are_strings
379-
@logger_debug
380369
def __update_local_nodes(self, nodes: Any) -> None:
381370
for node_id, node_data in nodes.items():
382371
node_attr_dict = self.node_attr_dict_factory()
@@ -386,7 +375,6 @@ def __update_local_nodes(self, nodes: Any) -> None:
386375
self.data[node_id] = node_attr_dict
387376

388377
@keys_are_strings
389-
@logger_debug
390378
def update(self, nodes: Any) -> None:
391379
"""g._node.update({'node/1': {'foo': 'bar'}, 'node/2': {'baz': 'qux'}})"""
392380
separated_by_collection = separate_nodes_by_collections(
@@ -414,15 +402,13 @@ def update(self, nodes: Any) -> None:
414402
logger.warning(m)
415403
raise ArangoDBBatchError(errors)
416404

417-
@logger_debug
418405
def values(self) -> Any:
419406
"""g._node.values()"""
420407
if not self.FETCHED_ALL_DATA:
421408
self._fetch_all()
422409

423410
yield from self.data.values()
424411

425-
@logger_debug
426412
def items(self, data: str | None = None, default: Any | None = None) -> Any:
427413
"""g._node.items() or G._node.items(data='foo')"""
428414
if data is None:
@@ -434,7 +420,6 @@ def items(self, data: str | None = None, default: Any | None = None) -> Any:
434420
v_cols = list(self.graph.vertex_collections())
435421
yield from aql_fetch_data(self.db, v_cols, data, default)
436422

437-
@logger_debug
438423
def _fetch_all(self):
439424
self.clear()
440425

nx_arangodb/classes/digraph.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ def __init__(
6464
self.clear_edges = self.clear_edges_override
6565
self.add_node = self.add_node_override
6666
self.remove_node = self.remove_node_override
67+
self.reverse = self.reverse_override
68+
69+
if (
70+
not self.is_multigraph()
71+
and incoming_graph_data is not None
72+
and not self._loaded_incoming_graph_data
73+
):
74+
nx.convert.to_networkx_graph(incoming_graph_data, create_using=self)
6775

6876
#######################
6977
# nx.DiGraph Overides #
@@ -79,6 +87,12 @@ def __init__(
7987
# def out_edges(self):
8088
# pass
8189

90+
def reverse_override(self, copy: bool = True) -> Any:
91+
if copy is False:
92+
raise NotImplementedError("In-place reverse is not supported yet.")
93+
94+
return super().reverse(copy=True)
95+
8296
def clear_edges_override(self):
8397
logger.info("Note that clearing edges ony erases the edges in the local cache")
8498
for predecessor_dict in self._pred.data.values():

nx_arangodb/classes/function.py

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from __future__ import annotations
77

8+
from collections import UserDict
89
from typing import Any, Callable, Generator, Optional, Tuple
910

1011
import networkx as nx
@@ -194,11 +195,11 @@ def wrapper(self: Any, key: Any, *args: Any, **kwargs: Any) -> Any:
194195
""""""
195196
if isinstance(key, str):
196197
if key != "-1" and "/" not in key:
197-
raise ValueError(f"{key} is not an ArangoDB ID.")
198+
raise KeyError(f"{key} is not an ArangoDB ID.")
198199

199200
elif isinstance(key, int):
200201
m = "Edge order is not guaranteed when using int as an edge key. It may raise a KeyError. Use at your own risk." # noqa
201-
logger.warning(m)
202+
logger.debug(m)
202203

203204
else:
204205
raise TypeError(f"{key} is not an ArangoDB Edge _id or integer.")
@@ -208,16 +209,6 @@ def wrapper(self: Any, key: Any, *args: Any, **kwargs: Any) -> Any:
208209
return wrapper
209210

210211

211-
def logger_debug(func: Callable[..., Any]) -> Any:
212-
"""Decorator to log debug messages."""
213-
214-
def wrapper(self: Any, *args: Any, **kwargs: Any) -> Any:
215-
logger.debug(f"{type(self)}.{func.__name__} - {args} - {kwargs}")
216-
return func(self, *args, **kwargs)
217-
218-
return wrapper
219-
220-
221212
def keys_are_strings(func: Callable[..., Any]) -> Any:
222213
"""Decorator to check if the keys are strings."""
223214

@@ -447,10 +438,10 @@ def aql_edge_count_src(
447438
direction: str,
448439
) -> int:
449440
query = f"""
450-
RETURN LENGTH(
451-
FOR v, e IN 1..1 {direction} @src_node_id GRAPH @graph_name
452-
RETURN DISTINCT e._id
453-
)
441+
FOR v, e IN 1..1 {direction} @src_node_id GRAPH @graph_name
442+
COLLECT id = e._id
443+
COLLECT WITH COUNT INTO num
444+
RETURN num
454445
"""
455446

456447
bind_vars = {
@@ -475,8 +466,9 @@ def aql_edge_count_src_dst(
475466
query = f"""
476467
FOR v, e IN 1..1 {direction} @src_node_id GRAPH @graph_name
477468
FILTER {filter_clause}
478-
COLLECT WITH COUNT INTO length
479-
RETURN length
469+
COLLECT id = e._id
470+
COLLECT WITH COUNT INTO num
471+
RETURN num
480472
"""
481473

482474
bind_vars = {

0 commit comments

Comments
 (0)