Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/networkx/classes/coreviews.py: 39%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

191 statements  

1"""Views of core data structures such as nested Mappings (e.g. dict-of-dicts). 

2These ``Views`` often restrict element access, with either the entire view or 

3layers of nested mappings being read-only. 

4""" 

5 

6from collections.abc import Mapping 

7 

8__all__ = [ 

9 "AtlasView", 

10 "AdjacencyView", 

11 "MultiAdjacencyView", 

12 "UnionAtlas", 

13 "UnionAdjacency", 

14 "UnionMultiInner", 

15 "UnionMultiAdjacency", 

16 "FilterAtlas", 

17 "FilterAdjacency", 

18 "FilterMultiInner", 

19 "FilterMultiAdjacency", 

20] 

21 

22 

23class AtlasView(Mapping): 

24 """An AtlasView is a Read-only Mapping of Mappings. 

25 

26 It is a View into a dict-of-dict data structure. 

27 The inner level of dict is read-write. But the 

28 outer level is read-only. 

29 

30 See Also 

31 ======== 

32 AdjacencyView: View into dict-of-dict-of-dict 

33 MultiAdjacencyView: View into dict-of-dict-of-dict-of-dict 

34 """ 

35 

36 __slots__ = ("_atlas",) 

37 

38 def __getstate__(self): 

39 return {"_atlas": self._atlas} 

40 

41 def __setstate__(self, state): 

42 self._atlas = state["_atlas"] 

43 

44 def __init__(self, d): 

45 self._atlas = d 

46 

47 def __len__(self): 

48 return len(self._atlas) 

49 

50 def __iter__(self): 

51 return iter(self._atlas) 

52 

53 def __getitem__(self, key): 

54 return self._atlas[key] 

55 

56 def copy(self): 

57 return {n: self[n].copy() for n in self._atlas} 

58 

59 def __str__(self): 

60 return str(self._atlas) # {nbr: self[nbr] for nbr in self}) 

61 

62 def __repr__(self): 

63 return f"{self.__class__.__name__}({self._atlas!r})" 

64 

65 

66class AdjacencyView(AtlasView): 

67 """An AdjacencyView is a Read-only Map of Maps of Maps. 

68 

69 It is a View into a dict-of-dict-of-dict data structure. 

70 The inner level of dict is read-write. But the 

71 outer levels are read-only. 

72 

73 See Also 

74 ======== 

75 AtlasView: View into dict-of-dict 

76 MultiAdjacencyView: View into dict-of-dict-of-dict-of-dict 

77 """ 

78 

79 __slots__ = () # Still uses AtlasView slots names _atlas 

80 

81 def __getitem__(self, name): 

82 return AtlasView(self._atlas[name]) 

83 

84 def copy(self): 

85 return {n: self[n].copy() for n in self._atlas} 

86 

87 

88class MultiAdjacencyView(AdjacencyView): 

89 """An MultiAdjacencyView is a Read-only Map of Maps of Maps of Maps. 

90 

91 It is a View into a dict-of-dict-of-dict-of-dict data structure. 

92 The inner level of dict is read-write. But the 

93 outer levels are read-only. 

94 

95 See Also 

96 ======== 

97 AtlasView: View into dict-of-dict 

98 AdjacencyView: View into dict-of-dict-of-dict 

99 """ 

100 

101 __slots__ = () # Still uses AtlasView slots names _atlas 

102 

103 def __getitem__(self, name): 

104 return AdjacencyView(self._atlas[name]) 

105 

106 def copy(self): 

107 return {n: self[n].copy() for n in self._atlas} 

108 

109 

110class UnionAtlas(Mapping): 

111 """A read-only union of two atlases (dict-of-dict). 

112 

113 The two dict-of-dicts represent the inner dict of 

114 an Adjacency: `G.succ[node]` and `G.pred[node]`. 

115 The inner level of dict of both hold attribute key:value 

116 pairs and is read-write. But the outer level is read-only. 

117 

118 See Also 

119 ======== 

120 UnionAdjacency: View into dict-of-dict-of-dict 

121 UnionMultiAdjacency: View into dict-of-dict-of-dict-of-dict 

122 """ 

123 

124 __slots__ = ("_succ", "_pred") 

125 

126 def __getstate__(self): 

127 return {"_succ": self._succ, "_pred": self._pred} 

128 

129 def __setstate__(self, state): 

130 self._succ = state["_succ"] 

131 self._pred = state["_pred"] 

132 

133 def __init__(self, succ, pred): 

134 self._succ = succ 

135 self._pred = pred 

136 

137 def __len__(self): 

138 return len(self._succ.keys() | self._pred.keys()) 

139 

140 def __iter__(self): 

141 return iter(set(self._succ.keys()) | set(self._pred.keys())) 

142 

143 def __getitem__(self, key): 

144 try: 

145 return self._succ[key] 

146 except KeyError: 

147 return self._pred[key] 

148 

149 def copy(self): 

150 result = {nbr: dd.copy() for nbr, dd in self._succ.items()} 

151 for nbr, dd in self._pred.items(): 

152 if nbr in result: 

153 result[nbr].update(dd) 

154 else: 

155 result[nbr] = dd.copy() 

156 return result 

157 

158 def __str__(self): 

159 return str({nbr: self[nbr] for nbr in self}) 

160 

161 def __repr__(self): 

162 return f"{self.__class__.__name__}({self._succ!r}, {self._pred!r})" 

163 

164 

165class UnionAdjacency(Mapping): 

166 """A read-only union of dict Adjacencies as a Map of Maps of Maps. 

167 

168 The two input dict-of-dict-of-dicts represent the union of 

169 `G.succ` and `G.pred`. Return values are UnionAtlas 

170 The inner level of dict is read-write. But the 

171 middle and outer levels are read-only. 

172 

173 succ : a dict-of-dict-of-dict {node: nbrdict} 

174 pred : a dict-of-dict-of-dict {node: nbrdict} 

175 The keys for the two dicts should be the same 

176 

177 See Also 

178 ======== 

179 UnionAtlas: View into dict-of-dict 

180 UnionMultiAdjacency: View into dict-of-dict-of-dict-of-dict 

181 """ 

182 

183 __slots__ = ("_succ", "_pred") 

184 

185 def __getstate__(self): 

186 return {"_succ": self._succ, "_pred": self._pred} 

187 

188 def __setstate__(self, state): 

189 self._succ = state["_succ"] 

190 self._pred = state["_pred"] 

191 

192 def __init__(self, succ, pred): 

193 # keys must be the same for two input dicts 

194 assert len(set(succ.keys()) ^ set(pred.keys())) == 0 

195 self._succ = succ 

196 self._pred = pred 

197 

198 def __len__(self): 

199 return len(self._succ) # length of each dict should be the same 

200 

201 def __iter__(self): 

202 return iter(self._succ) 

203 

204 def __getitem__(self, nbr): 

205 return UnionAtlas(self._succ[nbr], self._pred[nbr]) 

206 

207 def copy(self): 

208 return {n: self[n].copy() for n in self._succ} 

209 

210 def __str__(self): 

211 return str({nbr: self[nbr] for nbr in self}) 

212 

213 def __repr__(self): 

214 return f"{self.__class__.__name__}({self._succ!r}, {self._pred!r})" 

215 

216 

217class UnionMultiInner(UnionAtlas): 

218 """A read-only union of two inner dicts of MultiAdjacencies. 

219 

220 The two input dict-of-dict-of-dicts represent the union of 

221 `G.succ[node]` and `G.pred[node]` for MultiDiGraphs. 

222 Return values are UnionAtlas. 

223 The inner level of dict is read-write. But the outer levels are read-only. 

224 

225 See Also 

226 ======== 

227 UnionAtlas: View into dict-of-dict 

228 UnionAdjacency: View into dict-of-dict-of-dict 

229 UnionMultiAdjacency: View into dict-of-dict-of-dict-of-dict 

230 """ 

231 

232 __slots__ = () # Still uses UnionAtlas slots names _succ, _pred 

233 

234 def __getitem__(self, node): 

235 in_succ = node in self._succ 

236 in_pred = node in self._pred 

237 if in_succ: 

238 if in_pred: 

239 return UnionAtlas(self._succ[node], self._pred[node]) 

240 return UnionAtlas(self._succ[node], {}) 

241 return UnionAtlas({}, self._pred[node]) 

242 

243 def copy(self): 

244 nodes = set(self._succ.keys()) | set(self._pred.keys()) 

245 return {n: self[n].copy() for n in nodes} 

246 

247 

248class UnionMultiAdjacency(UnionAdjacency): 

249 """A read-only union of two dict MultiAdjacencies. 

250 

251 The two input dict-of-dict-of-dict-of-dicts represent the union of 

252 `G.succ` and `G.pred` for MultiDiGraphs. Return values are UnionAdjacency. 

253 The inner level of dict is read-write. But the outer levels are read-only. 

254 

255 See Also 

256 ======== 

257 UnionAtlas: View into dict-of-dict 

258 UnionMultiInner: View into dict-of-dict-of-dict 

259 """ 

260 

261 __slots__ = () # Still uses UnionAdjacency slots names _succ, _pred 

262 

263 def __getitem__(self, node): 

264 return UnionMultiInner(self._succ[node], self._pred[node]) 

265 

266 

267class FilterAtlas(Mapping): # nodedict, nbrdict, keydict 

268 """A read-only Mapping of Mappings with filtering criteria for nodes. 

269 

270 It is a view into a dict-of-dict data structure, and it selects only 

271 nodes that meet the criteria defined by ``NODE_OK``. 

272 

273 See Also 

274 ======== 

275 FilterAdjacency 

276 FilterMultiInner 

277 FilterMultiAdjacency 

278 """ 

279 

280 def __init__(self, d, NODE_OK): 

281 self._atlas = d 

282 self.NODE_OK = NODE_OK 

283 

284 def __len__(self): 

285 # check whether NODE_OK stores the number of nodes as `length` 

286 # or the nodes themselves as a set `nodes`. If not, count the nodes. 

287 if hasattr(self.NODE_OK, "length"): 

288 return self.NODE_OK.length 

289 if hasattr(self.NODE_OK, "nodes"): 

290 return len(self.NODE_OK.nodes & self._atlas.keys()) 

291 return sum(1 for n in self._atlas if self.NODE_OK(n)) 

292 

293 def __iter__(self): 

294 try: # check that NODE_OK has attr 'nodes' 

295 node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas) 

296 except AttributeError: 

297 node_ok_shorter = False 

298 if node_ok_shorter: 

299 return (n for n in self.NODE_OK.nodes if n in self._atlas) 

300 return (n for n in self._atlas if self.NODE_OK(n)) 

301 

302 def __getitem__(self, key): 

303 if key in self._atlas and self.NODE_OK(key): 

304 return self._atlas[key] 

305 raise KeyError(f"Key {key} not found") 

306 

307 def __str__(self): 

308 return str({nbr: self[nbr] for nbr in self}) 

309 

310 def __repr__(self): 

311 return f"{self.__class__.__name__}({self._atlas!r}, {self.NODE_OK!r})" 

312 

313 

314class FilterAdjacency(Mapping): # edgedict 

315 """A read-only Mapping of Mappings with filtering criteria for nodes and edges. 

316 

317 It is a view into a dict-of-dict-of-dict data structure, and it selects nodes 

318 and edges that satisfy specific criteria defined by ``NODE_OK`` and ``EDGE_OK``, 

319 respectively. 

320 

321 See Also 

322 ======== 

323 FilterAtlas 

324 FilterMultiInner 

325 FilterMultiAdjacency 

326 """ 

327 

328 def __init__(self, d, NODE_OK, EDGE_OK): 

329 self._atlas = d 

330 self.NODE_OK = NODE_OK 

331 self.EDGE_OK = EDGE_OK 

332 

333 def __len__(self): 

334 # check whether NODE_OK stores the number of nodes as `length` 

335 # or the nodes themselves as a set `nodes`. If not, count the nodes. 

336 if hasattr(self.NODE_OK, "length"): 

337 return self.NODE_OK.length 

338 if hasattr(self.NODE_OK, "nodes"): 

339 return len(self.NODE_OK.nodes & self._atlas.keys()) 

340 return sum(1 for n in self._atlas if self.NODE_OK(n)) 

341 

342 def __iter__(self): 

343 try: # check that NODE_OK has attr 'nodes' 

344 node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas) 

345 except AttributeError: 

346 node_ok_shorter = False 

347 if node_ok_shorter: 

348 return (n for n in self.NODE_OK.nodes if n in self._atlas) 

349 return (n for n in self._atlas if self.NODE_OK(n)) 

350 

351 def __getitem__(self, node): 

352 if node in self._atlas and self.NODE_OK(node): 

353 

354 def new_node_ok(nbr): 

355 return self.NODE_OK(nbr) and self.EDGE_OK(node, nbr) 

356 

357 return FilterAtlas(self._atlas[node], new_node_ok) 

358 raise KeyError(f"Key {node} not found") 

359 

360 def __str__(self): 

361 return str({nbr: self[nbr] for nbr in self}) 

362 

363 def __repr__(self): 

364 name = self.__class__.__name__ 

365 return f"{name}({self._atlas!r}, {self.NODE_OK!r}, {self.EDGE_OK!r})" 

366 

367 

368class FilterMultiInner(FilterAdjacency): # muliedge_seconddict 

369 """A read-only Mapping of Mappings with filtering criteria for nodes and edges. 

370 

371 It is a view into a dict-of-dict-of-dict-of-dict data structure, and it selects nodes 

372 and edges that meet specific criteria defined by ``NODE_OK`` and ``EDGE_OK``. 

373 

374 See Also 

375 ======== 

376 FilterAtlas 

377 FilterAdjacency 

378 FilterMultiAdjacency 

379 """ 

380 

381 def __iter__(self): 

382 try: # check that NODE_OK has attr 'nodes' 

383 node_ok_shorter = 2 * len(self.NODE_OK.nodes) < len(self._atlas) 

384 except AttributeError: 

385 node_ok_shorter = False 

386 if node_ok_shorter: 

387 my_nodes = (n for n in self.NODE_OK.nodes if n in self._atlas) 

388 else: 

389 my_nodes = (n for n in self._atlas if self.NODE_OK(n)) 

390 for n in my_nodes: 

391 some_keys_ok = False 

392 for key in self._atlas[n]: 

393 if self.EDGE_OK(n, key): 

394 some_keys_ok = True 

395 break 

396 if some_keys_ok is True: 

397 yield n 

398 

399 def __getitem__(self, nbr): 

400 if ( 

401 nbr in self._atlas 

402 and self.NODE_OK(nbr) 

403 and any(self.EDGE_OK(nbr, key) for key in self._atlas[nbr]) 

404 ): 

405 

406 def new_node_ok(key): 

407 return self.EDGE_OK(nbr, key) 

408 

409 return FilterAtlas(self._atlas[nbr], new_node_ok) 

410 raise KeyError(f"Key {nbr} not found") 

411 

412 

413class FilterMultiAdjacency(FilterAdjacency): # multiedgedict 

414 """A read-only Mapping of Mappings with filtering criteria 

415 for nodes and edges. 

416 

417 It is a view into a dict-of-dict-of-dict-of-dict data structure, 

418 and it selects nodes and edges that satisfy specific criteria 

419 defined by ``NODE_OK`` and ``EDGE_OK``, respectively. 

420 

421 See Also 

422 ======== 

423 FilterAtlas 

424 FilterAdjacency 

425 FilterMultiInner 

426 """ 

427 

428 def __getitem__(self, node): 

429 if node in self._atlas and self.NODE_OK(node): 

430 

431 def edge_ok(nbr, key): 

432 return self.NODE_OK(nbr) and self.EDGE_OK(node, nbr, key) 

433 

434 return FilterMultiInner(self._atlas[node], self.NODE_OK, edge_ok) 

435 raise KeyError(f"Key {node} not found")