go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/lucicfg/graph/graph.go (about) 1 // Copyright 2018 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package graph 16 17 import ( 18 "container/list" 19 "fmt" 20 "sort" 21 22 "go.starlark.net/starlark" 23 "go.starlark.net/starlarkstruct" 24 25 "go.chromium.org/luci/common/errors" 26 "go.chromium.org/luci/starlark/builtins" 27 ) 28 29 var ( 30 // ErrFinalized is returned by Graph methods that modify the graph when they 31 // are used on a finalized graph. 32 ErrFinalized = errors.New("cannot modify a finalized graph") 33 34 // ErrNotFinalized is returned by Graph traversal/query methods when they are 35 // used with a non-finalized graph. 36 ErrNotFinalized = errors.New("cannot query a graph under construction") 37 ) 38 39 // backtrace returns an error message annotated with a backtrace. 40 func backtrace(err error, t *builtins.CapturedStacktrace) string { 41 return t.String() + "Error: " + err.Error() 42 } 43 44 // NodeRedeclarationError is returned when a adding an existing node. 45 type NodeRedeclarationError struct { 46 Trace *builtins.CapturedStacktrace // where it is added second time 47 Previous *Node // the previously added node 48 } 49 50 // Error is part of 'error' interface. 51 func (e *NodeRedeclarationError) Error() string { 52 // TODO(vadimsh): Improve error messages. 53 return fmt.Sprintf("%s is redeclared, previous declaration:\n%s", e.Previous, e.Previous.Trace) 54 } 55 56 // Backtrace returns an error message with a backtrace where it happened. 57 func (e *NodeRedeclarationError) Backtrace() string { 58 return backtrace(e, e.Trace) 59 } 60 61 // CycleError is returned when adding an edge that introduces a cycle. 62 // 63 // Nodes referenced by edges may not be fully declared yet (i.e. they may not 64 // yet have properties or trace associated with them). They do have valid keys 65 // though. 66 type CycleError struct { 67 Trace *builtins.CapturedStacktrace // where the edge is being added 68 Edge *Edge // an edge being added 69 Path []*Edge // the rest of the cycle 70 } 71 72 // Error is part of 'error' interface. 73 func (e *CycleError) Error() string { 74 // TODO(vadimsh): Improve error messages. 75 return fmt.Sprintf("relation %q between %s and %s introduces a cycle", 76 e.Edge.Title, e.Edge.Parent, e.Edge.Child) 77 } 78 79 // Backtrace returns an error message with a backtrace where it happened. 80 func (e *CycleError) Backtrace() string { 81 return backtrace(e, e.Trace) 82 } 83 84 // DanglingEdgeError is returned by Finalize if a graph has an edge whose parent 85 // or child (or both) haven't been declared by AddNode. 86 // 87 // Use Edge.(Parent|Child).Declared() to figure out which end is not defined. 88 type DanglingEdgeError struct { 89 Edge *Edge 90 } 91 92 // Error is part of 'error' interface. 93 func (e *DanglingEdgeError) Error() string { 94 rel := "" 95 if e.Edge.Title != "" { 96 rel = fmt.Sprintf(" in %q", e.Edge.Title) 97 } 98 99 // TODO(vadimsh): Improve error messages. 100 hasP := e.Edge.Parent.Declared() 101 hasC := e.Edge.Child.Declared() 102 switch { 103 case hasP && hasC: 104 // This should not happen. 105 return "incorrect DanglingEdgeError, the edge is fully connected" 106 case !hasP && hasC: 107 return fmt.Sprintf("%s%s refers to undefined %s", 108 e.Edge.Child, rel, e.Edge.Parent) 109 case hasP && !hasC: 110 return fmt.Sprintf("%s%s refers to undefined %s", 111 e.Edge.Parent, rel, e.Edge.Child) 112 default: 113 return fmt.Sprintf("relation %q: refers to %s and %s, neither is defined", 114 e.Edge.Title, e.Edge.Parent, e.Edge.Child) 115 } 116 } 117 118 // Backtrace returns an error message with a backtrace where it happened. 119 func (e *DanglingEdgeError) Backtrace() string { 120 return backtrace(e, e.Edge.Trace) 121 } 122 123 // Graph is a DAG of keyed nodes. 124 // 125 // It is initially in "under construction" state, in which callers can use 126 // AddNode and AddEdge (in any order) to build the graph, but can't yet query 127 // it. 128 // 129 // Once the construction is complete, the graph should be finalized via 130 // Finalize() call, which checks there are no dangling edges and freezes the 131 // graph (so AddNode/AddEdge return errors), making it queryable. 132 // 133 // Graph implements starlark.HasAttrs interface and have the following methods: 134 // - key(kind1: string, id1: string, kind2: string, id2: string, ...): graph.key 135 // - add_node(key: graph.key, props={}, idempotent=False, trace=stacktrace()): graph.node 136 // - add_edge(parent: graph.Key, child: graph.Key, title=”, trace=stacktrace()) 137 // - finalize(): []string 138 // - node(key: graph.key): graph.node 139 // - children(parent: graph.key, order_by='key'): []graph.node 140 // - descendants(root: graph.key, callback=None, order_by='key', topology='breadth'): []graph.node 141 // - parents(child: graph.key, order_by='key'): []graph.node 142 // - sorted_nodes(nodes: iterable<graph.node>, order_by='key'): []graph.node 143 type Graph struct { 144 KeySet 145 146 nodes map[*Key]*Node // all declared and "predeclared" nodes 147 edges []*Edge // all defined edges, in their definition order 148 nextIndex int // index to assign to the next node, for ordering 149 finalized bool // true if the graph is no longer mutable 150 } 151 152 // Visitor visits a node, looks at next possible candidates for a visit and 153 // returns ones that it really wants to visit. 154 type Visitor func(n *Node, next []*Node) ([]*Node, error) 155 156 // validateOrder returns an error if the edge traversal order is unrecognized. 157 func validateOrder(orderBy string) error { 158 switch orderBy { 159 case "key", "~key", "def", "~def": 160 return nil 161 } 162 return fmt.Errorf("unknown order %q, expecting one of \"key\", \"~key\", \"def\", \"~def\"", orderBy) 163 } 164 165 // validateTopology returns an error if the traversal topology is unrecognized. 166 func validateTopology(topology string) error { 167 if topology != "breadth" && topology != "depth" { 168 return fmt.Errorf("unknown topology %q, expecting either \"breadth\" or \"depth\"", topology) 169 } 170 return nil 171 } 172 173 // validateKey returns an error if the key is not from this graph. 174 func (g *Graph) validateKey(title string, k *Key) error { 175 if k.set != &g.KeySet { 176 return fmt.Errorf("bad %s: %s is from another graph", title, k) 177 } 178 return nil 179 } 180 181 // initNode either returns an existing *Node, or adds a new one. 182 // 183 // The newly added node is in "predeclared" state: it has no Props or Trace. 184 // A predeclared node is moved to the fully declared state by AddNode, this can 185 // happen only once for non-idempotent nodes or more than once for idempotent 186 // nodes, if each time their props dict is exactly same. 187 // 188 // Panics if the graph is finalized. 189 func (g *Graph) initNode(k *Key) *Node { 190 if g.finalized { 191 panic(ErrFinalized) 192 } 193 if n := g.nodes[k]; n != nil { 194 return n 195 } 196 if g.nodes == nil { 197 g.nodes = make(map[*Key]*Node, 1) 198 } 199 n := &Node{Key: k} 200 g.nodes[k] = n 201 return n 202 } 203 204 //// API to mutate the graph before it is finalized. 205 206 // AddNode adds a node to the graph. 207 // 208 // If such node already exists, either returns an error right away (if 209 // 'idempotent' is false), or verifies the existing node has also been marked 210 // as idempotent and has exact same props dict as being passed here. 211 // 212 // Trying to use AddNode after the graph has been finalized is an error. 213 // 214 // Freezes props.values() as a side effect. 215 func (g *Graph) AddNode(k *Key, props *starlark.Dict, idempotent bool, trace *builtins.CapturedStacktrace) error { 216 if g.finalized { 217 return ErrFinalized 218 } 219 if err := g.validateKey("key", k); err != nil { 220 return err 221 } 222 223 // Only string keys are allowed in 'props'. 224 for _, pk := range props.Keys() { 225 if _, ok := pk.(starlark.String); !ok { 226 return fmt.Errorf("non-string key %s in 'props'", pk) 227 } 228 } 229 propsStruct := starlarkstruct.FromKeywords(starlark.String("props"), props.Items()) 230 231 n := g.initNode(k) 232 if !n.Declared() { 233 n.declare(g.nextIndex, propsStruct, idempotent, trace) 234 g.nextIndex++ 235 return nil 236 } 237 238 // Only idempotent nodes can be redeclared, and only if all declarations 239 // are marked as idempotent and they specify exact same props. 240 if n.Idempotent && idempotent { 241 switch eq, err := starlark.Equal(propsStruct, n.Props); { 242 case err != nil: 243 return err 244 case eq: 245 return nil 246 } 247 } 248 return &NodeRedeclarationError{Trace: trace, Previous: n} 249 } 250 251 // AddEdge adds an edge to the graph. 252 // 253 // Trying to use AddEdge after the graph has been finalized is an error. 254 // 255 // Neither of the nodes have to exist yet: it is OK to declare nodes and edges 256 // in arbitrary order as long as at the end of the graph construction (when it 257 // is finalized) the graph is complete. 258 // 259 // It is OK to add the same edge (with the same title) more than once. Only 260 // the trace of the first definition is recorded. 261 // 262 // Returns an error if the new edge introduces a cycle. 263 func (g *Graph) AddEdge(parent, child *Key, title string, trace *builtins.CapturedStacktrace) error { 264 if g.finalized { 265 return ErrFinalized 266 } 267 if err := g.validateKey("parent", parent); err != nil { 268 return err 269 } 270 if err := g.validateKey("child", child); err != nil { 271 return err 272 } 273 274 edge := &Edge{ 275 Parent: g.initNode(parent), 276 Child: g.initNode(child), 277 Title: title, 278 Trace: trace, 279 } 280 281 // Have this exact edge already? This is fine. 282 for _, e := range edge.Parent.children { 283 if e.Child == edge.Child && e.Title == title { 284 return nil 285 } 286 } 287 288 // The child may have the parent among its descendants already? Then adding 289 // the edge would introduce a cycle. 290 err := edge.Child.visitDescendants(nil, func(n *Node, path []*Edge) error { 291 if n == edge.Parent { 292 return &CycleError{ 293 Trace: trace, 294 Edge: edge, 295 Path: append([]*Edge(nil), path...), 296 } 297 } 298 return nil 299 }) 300 if err != nil { 301 return err 302 } 303 304 edge.Parent.children = append(edge.Parent.children, edge) 305 edge.Child.parents = append(edge.Child.parents, edge) 306 g.edges = append(g.edges, edge) 307 return nil 308 } 309 310 // Finalize finishes the graph construction by verifying there are no "dangling" 311 // edges: all edges ever added by AddEdge should connect nodes that were at some 312 // point defined by AddNode. 313 // 314 // Finalizing an already finalized graph is not an error. 315 // 316 // A finalized graph is immutable (and frozen in Starlark sense): all subsequent 317 // calls to AddNode/AddEdge return an error. Conversely, freezing the graph via 318 // Freeze() finalizes it, panicking if the finalization fails. Users of Graph 319 // are expected to finalize the graph themselves (checking errors) before 320 // Starlark tries to freeze it. 321 // 322 // Once finalized, the graph becomes queryable. 323 func (g *Graph) Finalize() (errs errors.MultiError) { 324 if g.finalized { 325 return 326 } 327 328 for _, e := range g.edges { 329 if !e.Parent.Declared() || !e.Child.Declared() { 330 errs = append(errs, &DanglingEdgeError{Edge: e}) 331 } 332 } 333 334 g.finalized = len(errs) == 0 335 return 336 } 337 338 //// API to query the graph after it is finalized. 339 340 // Node returns a node by the key or (nil, nil) if there's no such node. 341 // 342 // Trying to use Node before the graph has been finalized is an error. 343 func (g *Graph) Node(k *Key) (*Node, error) { 344 if !g.finalized { 345 return nil, ErrNotFinalized 346 } 347 if err := g.validateKey("key", k); err != nil { 348 return nil, err 349 } 350 switch n := g.nodes[k]; { 351 case n == nil: 352 return nil, nil 353 case !n.Declared(): 354 panic(fmt.Errorf("impossible not-yet-declared node in a finalized graph: %s", n.Key)) 355 default: 356 return n, nil 357 } 358 } 359 360 // Children returns direct children of a node (given by its key). 361 // 362 // The order of the result depends on a value of 'orderBy': 363 // 364 // 'key': nodes are ordered lexicographically by their keys (smaller first). 365 // 'def': nodes are ordered by the order edges to them were defined during 366 // the execution (earlier first). 367 // 368 // Any other value of 'orderBy' causes an error. 369 // 370 // A missing node is considered to have no children. 371 // 372 // Trying to use Children before the graph has been finalized is an error. 373 func (g *Graph) Children(parent *Key, orderBy string) ([]*Node, error) { 374 return g.orderedRelatives(parent, "parent", orderBy, (*Node).listChildren) 375 } 376 377 // Descendants recursively visits 'root' and all its children, in breadth or 378 // depth first order. 379 // 380 // Returns the list of all visited nodes, in order they were visited, including 381 // 'root' itself. If 'root' is missing, returns an empty list. 382 // 383 // The order of enumeration of direct children of a node depends on a value of 384 // 'orderBy': 385 // 386 // 'key': nodes are ordered lexicographically by their keys (smaller first). 387 // 'def': nodes are ordered by the order edges to them were defined during 388 // the execution (earlier first). 389 // 390 // '~' in front (i.e. ~key' and '~def') means "reverse order". 391 // 392 // Any other value of 'orderBy' causes an error. 393 // 394 // Each node is visited only once, even if it is reachable through multiple 395 // paths. Note that the graph has no cycles (by construction). 396 // 397 // The visitor callback (if not nil) is called for each visited node. It decides 398 // what children to visit next. The callback always sees all children of the 399 // node, even if some of them (or all) have already been visited. Visited nodes 400 // will be skipped even if the visitor returns them. 401 // 402 // Trying to use Descendants before the graph has been finalized is an error. 403 func (g *Graph) Descendants(root *Key, orderBy, topology string, visitor Visitor) ([]*Node, error) { 404 if !g.finalized { 405 return nil, ErrNotFinalized 406 } 407 if err := g.validateKey("root", root); err != nil { 408 return nil, err 409 } 410 if err := validateOrder(orderBy); err != nil { 411 return nil, err 412 } 413 if err := validateTopology(topology); err != nil { 414 return nil, err 415 } 416 417 rootNode := g.nodes[root] 418 if rootNode == nil { 419 return nil, nil 420 } 421 422 switch topology { 423 case "breadth": 424 return descBreadth(rootNode, orderBy, visitor) 425 case "depth": 426 return descDepth(rootNode, orderBy, visitor) 427 default: 428 panic("impossible") 429 } 430 } 431 432 // descBreadth implements non-recursive breadth-first traversal. 433 func descBreadth(root *Node, orderBy string, visitor Visitor) ([]*Node, error) { 434 queue := list.New() 435 queuedSet := make(map[*Node]struct{}, 1) 436 visited := make([]*Node, 0, 1) 437 438 enqueue := func(n *Node) { 439 if _, yes := queuedSet[n]; !yes { 440 queue.PushBack(n) 441 queuedSet[n] = struct{}{} 442 } 443 } 444 enqueue(root) 445 446 for queue.Len() > 0 { 447 cur := queue.Remove(queue.Front()).(*Node) 448 visited = append(visited, cur) 449 450 // Ask the visitor callback to decide where it wants to go next. 451 next, err := filteredChildren(cur, orderBy, visitor) 452 if err != nil { 453 return nil, err 454 } 455 456 // Go there (eventually). 457 for _, n := range next { 458 enqueue(n) 459 } 460 } 461 return visited, nil 462 } 463 464 // descDepth implements recursive depth-first traversal. 465 func descDepth(root *Node, orderBy string, visitor Visitor) ([]*Node, error) { 466 visited := make([]*Node, 0, 1) 467 visitedSet := make(map[*Node]struct{}, 1) 468 469 var visit func(root *Node) error 470 visit = func(root *Node) error { 471 // Been here before? 472 if _, yes := visitedSet[root]; yes { 473 return nil 474 } 475 visitedSet[root] = struct{}{} 476 477 // Ask the visitor callback to decide where it wants to go next. 478 next, err := filteredChildren(root, orderBy, visitor) 479 if err != nil { 480 return err 481 } 482 483 // Go there, right now. 484 for _, n := range next { 485 if err := visit(n); err != nil { 486 return err 487 } 488 } 489 490 // Now that all children are visited, emit the root itself too. 491 visited = append(visited, root) 492 return nil 493 } 494 495 if err := visit(root); err != nil { 496 return nil, err 497 } 498 return visited, nil 499 } 500 501 // filteredChildren calls 'visitor' callback to filter the list of children. 502 // 503 // Used by Descendants implementation. 504 func filteredChildren(cur *Node, orderBy string, visitor Visitor) ([]*Node, error) { 505 children := sortByEdgeOrder(cur.listChildren(), orderBy) 506 if visitor == nil { 507 return children, nil 508 } 509 // Ask the visitor callback to decide where it wants to go next. Verify the 510 // callback didn't sneakily return some *Node it saved somewhere before. 511 // This may cause infinite recursion and other weird stuff. 512 next, err := visitor(cur, children) 513 if err != nil { 514 return nil, err 515 } 516 allowed := make(map[*Node]struct{}, len(children)) 517 for _, n := range children { 518 allowed[n] = struct{}{} 519 } 520 for _, n := range next { 521 if _, yes := allowed[n]; !yes { 522 return nil, fmt.Errorf("the callback unexpectedly returned %s which is not a child of %s", n, cur) 523 } 524 } 525 return next, nil 526 } 527 528 // Parents returns direct parents of a node (given by its key). 529 // 530 // The order of the result depends on a value of 'orderBy': 531 // 532 // 'key': nodes are ordered lexicographically by their keys (smaller first). 533 // 'def': nodes are ordered by the order edges to them were defined during 534 // the execution (earlier first). 535 // 536 // Any other value of 'orderBy' causes an error. 537 // 538 // A missing node is considered to have no parents. 539 // 540 // Trying to use Parents before the graph has been finalized is an error. 541 func (g *Graph) Parents(child *Key, orderBy string) ([]*Node, error) { 542 return g.orderedRelatives(child, "child", orderBy, (*Node).listParents) 543 } 544 545 // SortNodes sorts a slice of nodes of this graph in-place. 546 // 547 // The order of the result depends on the value of 'orderBy': 548 // 549 // 'key': nodes are ordered lexicographically by their keys (smaller first). 550 // 'def': nodes are ordered by the order they were defined in the graph. 551 // 552 // Any other value of 'orderBy' causes an error. 553 func (g *Graph) SortNodes(nodes []*Node, orderBy string) error { 554 if err := validateOrder(orderBy); err != nil { 555 return err 556 } 557 558 // Only nodes from the same graph are comparable to each other. Comparing 559 // .Index from different graphs makes no sense. 560 for _, n := range nodes { 561 if !n.BelongsTo(g) { 562 return fmt.Errorf("bad node %s - from another graph", n) 563 } 564 } 565 566 switch orderBy { 567 case "def": 568 sort.Slice(nodes, func(i, j int) bool { return nodes[i].Index < nodes[j].Index }) 569 case "key": 570 sort.Slice(nodes, func(i, j int) bool { return nodes[i].Key.Less(nodes[j].Key) }) 571 default: 572 // Must not happen, orderBy must already be validated here. 573 panic(fmt.Sprintf("unknown order %q", orderBy)) 574 } 575 return nil 576 } 577 578 // orderedRelatives is a common implementation of Children and Parents. 579 func (g *Graph) orderedRelatives(key *Key, attr, orderBy string, cb func(n *Node) []*Node) ([]*Node, error) { 580 if !g.finalized { 581 return nil, ErrNotFinalized 582 } 583 if err := g.validateKey(attr, key); err != nil { 584 return nil, err 585 } 586 if err := validateOrder(orderBy); err != nil { 587 return nil, err 588 } 589 if n := g.nodes[key]; n != nil { 590 return sortByEdgeOrder(cb(n), orderBy), nil 591 } 592 return nil, nil // no node at all -> no related nodes 593 } 594 595 // sortByEdgeOrder orders either children or parents of some node according to 596 // the order of edges to/from them. 597 // 598 // Assumes 'nodes' came from either .listChildren() or .listParents(), and thus 599 // are mutable copies, which are already ordered by 'def' order. 600 // 601 // Note that 'def' order here means "in order edges were defined". This is 602 // different from "in order nodes themselves were defined". This is different 603 // from the meaning of 'def' order in SortNodes. 604 func sortByEdgeOrder(nodes []*Node, orderBy string) []*Node { 605 switch orderBy { 606 case "def": 607 // default 608 case "~def": 609 for i, j := 0, len(nodes)-1; i < j; i, j = i+1, j-1 { 610 nodes[i], nodes[j] = nodes[j], nodes[i] 611 } 612 case "key": 613 sort.Slice(nodes, func(i, j int) bool { return nodes[i].Key.Less(nodes[j].Key) }) 614 case "~key": 615 sort.Slice(nodes, func(i, j int) bool { return nodes[j].Key.Less(nodes[i].Key) }) 616 default: 617 // Must not happen, orderBy must already be validated here. 618 panic(fmt.Sprintf("unknown order %q", orderBy)) 619 } 620 return nodes 621 } 622 623 //// starlark.Value interface implementation. 624 625 // String is a part of starlark.Value interface 626 func (g *Graph) String() string { return "graph" } 627 628 // Type is a part of starlark.Value interface. 629 func (g *Graph) Type() string { return "graph" } 630 631 // Freeze is a part of starlark.Value interface. 632 // 633 // It finalizes the graph, panicking on errors. Users of Graph are expected to 634 // finalize the graph themselves (checking errors) before Starlark tries to 635 // freeze it. 636 func (g *Graph) Freeze() { 637 if err := g.Finalize(); err != nil { 638 panic(err) 639 } 640 } 641 642 // Truth is a part of starlark.Value interface. 643 func (g *Graph) Truth() starlark.Bool { return starlark.True } 644 645 // Hash is a part of starlark.Value interface. 646 func (g *Graph) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable") } 647 648 // AttrNames is a part of starlark.HasAttrs interface. 649 func (g *Graph) AttrNames() []string { 650 names := make([]string, 0, len(graphAttrs)) 651 for k := range graphAttrs { 652 names = append(names, k) 653 } 654 sort.Strings(names) 655 return names 656 } 657 658 // Attr is a part of starlark.HasAttrs interface. 659 func (g *Graph) Attr(name string) (starlark.Value, error) { 660 impl, ok := graphAttrs[name] 661 if !ok { 662 return nil, nil // per Attr(...) contract 663 } 664 return impl.BindReceiver(g), nil 665 } 666 667 //// Starlark bindings for individual graph methods. 668 669 func nodesList(nodes []*Node) *starlark.List { 670 vals := make([]starlark.Value, len(nodes)) 671 for i, n := range nodes { 672 vals[i] = n 673 } 674 return starlark.NewList(vals) 675 } 676 677 var graphAttrs = map[string]*starlark.Builtin{ 678 // key(kind1: string, id1: string, kind2: string, id2: string, ...): graph.key 679 "key": starlark.NewBuiltin("key", func(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 680 if len(kwargs) != 0 { 681 return nil, fmt.Errorf("graph.key: not expecting keyword arguments") 682 } 683 pairs := make([]string, len(args)) 684 for idx, arg := range args { 685 str, ok := arg.(starlark.String) 686 if !ok { 687 return nil, fmt.Errorf("graph.key: all arguments must be strings, arg #%d was %s", idx, arg.Type()) 688 } 689 pairs[idx] = str.GoString() 690 } 691 return b.Receiver().(*Graph).Key(pairs...) 692 }), 693 694 // add_node(key: graph.key, props={}, idempotent=False, trace=stacktrace()): graph.node 695 "add_node": starlark.NewBuiltin("add_node", func(th *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 696 var key *Key 697 var props *starlark.Dict 698 var idempotent starlark.Bool 699 var trace *builtins.CapturedStacktrace 700 err := starlark.UnpackArgs("add_node", args, kwargs, 701 "key", &key, 702 "props?", &props, 703 "idempotent?", &idempotent, 704 "trace?", &trace, 705 ) 706 if err != nil { 707 return nil, err 708 } 709 if props == nil { 710 props = &starlark.Dict{} 711 } 712 if trace == nil { 713 var err error 714 if trace, err = builtins.CaptureStacktrace(th, 0); err != nil { 715 return nil, err 716 } 717 } 718 err = b.Receiver().(*Graph).AddNode(key, props, bool(idempotent), trace) 719 return starlark.None, err 720 }), 721 722 // add_edge(parent: graph.Key, child: graph.Key, title='', trace=stacktrace()) 723 "add_edge": starlark.NewBuiltin("add_edge", func(th *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 724 var parent *Key 725 var child *Key 726 var title starlark.String 727 var trace *builtins.CapturedStacktrace 728 err := starlark.UnpackArgs("add_edge", args, kwargs, 729 "parent", &parent, 730 "child", &child, 731 "title?", &title, 732 "trace?", &trace) 733 if err != nil { 734 return nil, err 735 } 736 737 if trace == nil { 738 var err error 739 if trace, err = builtins.CaptureStacktrace(th, 0); err != nil { 740 return nil, err 741 } 742 } 743 744 err = b.Receiver().(*Graph).AddEdge(parent, child, title.GoString(), trace) 745 return starlark.None, err 746 }), 747 748 // finalize(): []string 749 "finalize": starlark.NewBuiltin("finalize", func(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 750 if err := starlark.UnpackArgs("finalize", args, kwargs); err != nil { 751 return nil, err 752 } 753 errs := b.Receiver().(*Graph).Finalize() 754 out := make([]starlark.Value, len(errs)) 755 for i, err := range errs { 756 out[i] = starlark.String(err.Error()) 757 } 758 return starlark.NewList(out), nil 759 }), 760 761 // node(key: graph.key): graph.node 762 "node": starlark.NewBuiltin("node", func(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 763 var key *Key 764 if err := starlark.UnpackArgs("node", args, kwargs, "key", &key); err != nil { 765 return nil, err 766 } 767 if node, err := b.Receiver().(*Graph).Node(key); node != nil || err != nil { 768 return node, err 769 } 770 return starlark.None, nil 771 }), 772 773 // children(parent: graph.key, order_by='key'): []graph.node 774 "children": starlark.NewBuiltin("children", func(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 775 var parent *Key 776 var orderBy starlark.String = "key" 777 err := starlark.UnpackArgs("children", args, kwargs, 778 "parent", &parent, 779 "order_by?", &orderBy) 780 if err != nil { 781 return nil, err 782 } 783 nodes, err := b.Receiver().(*Graph).Children(parent, orderBy.GoString()) 784 if err != nil { 785 return nil, err 786 } 787 return nodesList(nodes), nil 788 }), 789 790 // descendants(root: graph.key, callback=None, order_by='key'): []graph.Node. 791 // 792 // where 'callback' is func(node graph.node, children []graph.node): []graph.node. 793 "descendants": starlark.NewBuiltin("descendants", func(th *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 794 var root *Key 795 var callback starlark.Value 796 var orderBy starlark.String = "key" 797 var topology starlark.String = "breadth" 798 err := starlark.UnpackArgs("descendants", args, kwargs, 799 "root", &root, 800 "callback?", &callback, 801 "order_by?", &orderBy, 802 "topology?", &topology) 803 if err != nil { 804 return nil, err 805 } 806 807 // Glue layer between Go callback and Starlark callback. 808 var visitor Visitor 809 if callback != nil && callback != starlark.None { 810 visitor = func(node *Node, children []*Node) ([]*Node, error) { 811 // Call callback(node, children). 812 ret, err := starlark.Call(th, callback, starlark.Tuple{node, nodesList(children)}, nil) 813 if err != nil { 814 return nil, err 815 } 816 // The callback is expected to return a list. 817 lst, _ := ret.(*starlark.List) 818 if lst == nil { 819 return nil, fmt.Errorf( 820 "descendants: callback %s unexpectedly returned %s instead of a list", 821 callback, ret.Type()) 822 } 823 // And it should be a list of nodes. 824 out := make([]*Node, lst.Len()) 825 for i := range out { 826 if out[i], _ = lst.Index(i).(*Node); out[i] == nil { 827 return nil, fmt.Errorf( 828 "descendants: callback %s unexpectedly returned %s as element #%d instead of a graph.node", 829 callback, lst.Index(i).Type(), i) 830 } 831 } 832 return out, nil 833 } 834 } 835 836 nodes, err := b.Receiver().(*Graph).Descendants(root, orderBy.GoString(), topology.GoString(), visitor) 837 if err != nil { 838 return nil, err 839 } 840 return nodesList(nodes), nil 841 }), 842 843 // parents(child: graph.key, order_by='key'): []graph.node 844 "parents": starlark.NewBuiltin("parents", func(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 845 var child *Key 846 var orderBy starlark.String = "key" 847 err := starlark.UnpackArgs("parents", args, kwargs, 848 "child", &child, 849 "order_by?", &orderBy) 850 if err != nil { 851 return nil, err 852 } 853 nodes, err := b.Receiver().(*Graph).Parents(child, orderBy.GoString()) 854 if err != nil { 855 return nil, err 856 } 857 return nodesList(nodes), nil 858 }), 859 860 // sorted_nodes(nodes: iterable<graph.node>, order_by='key'): []graph.node 861 "sorted_nodes": starlark.NewBuiltin("sorted_nodes", func(_ *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 862 var nodes starlark.Iterable 863 var orderBy starlark.String = "key" 864 err := starlark.UnpackArgs("sorted_nodes", args, kwargs, 865 "nodes", &nodes, 866 "order_by?", &orderBy) 867 if err != nil { 868 return nil, err 869 } 870 871 iter := nodes.Iterate() 872 defer iter.Done() 873 874 var toSort []*Node 875 var val starlark.Value 876 for iter.Next(&val) { 877 node, ok := val.(*Node) 878 if !ok { 879 return nil, fmt.Errorf("sorted_nodes: got %s, expecting graph.node", val) 880 } 881 toSort = append(toSort, node) 882 } 883 884 if err := b.Receiver().(*Graph).SortNodes(toSort, orderBy.GoString()); err != nil { 885 return nil, err 886 } 887 return nodesList(toSort), nil 888 }), 889 }