7.4.2. Class Graph

Objects of class Graph wrap RFGs (resource flow graphs). An Graph object allows performing operations on the common state of the RFG. You can load and save RFGs. You can add, rename and delete views of the RFG. You can get lists of all valid attributes, node types, and edge types. You can access and change the values of graph attributes. And you can get a set of all nodes respectively edges within a view of the RFG matching a predicate.

7.4.2.1. Obtaining a Graph

class Graph([filename])

The constructor of class Graph called without parameters creates a new, empty RFG with a schema for C. A single parameter is interpreted as filename. The file has to exist [1] and contain a valid RFG. Listing Loading, creating, and saving an RFG shows how to obtain an RFG.

Loading, creating, and saving an RFG
#!/usr/bin/env rfgscript
import sys
from bauhaus import rfg

# loads the RFG file given as first command line parameter
my_rfg = rfg.Graph(sys.argv[1])

# creates a new, empty graph
some_rfg = rfg.Graph()

# saves the new graph
# name is the second command line parameter
some_rfg.save(sys.argv[2])

Note

Scripts running in Gravis can also obtain the currently loaded RFG via the Gravis scripting interface gravis.get_graph(). See Section The RFG Displayed in Gravis

7.4.2.2. Saving a Graph

save([filename])

Instance method save saves the graph to a file. If filename already exists, it is silently overwritten. If filename is not writable or otherwise invalid, an exception is raised describing the details why the operation failed. If the RFG was loaded and not constructed, then the filename is known and can be omitted if overwriting the input filename is intended.

7.4.2.3. Destroying a Graph

destroy()

If you do not need an RFG anymore, you can get rid of it by using instance method destroy. RFGs are not automatically garbage collected.

Imagine you have to handle multiple RFGs, one after another. You could do it as shown in Listing Destroying an RFG.

Destroying an RFG
from bauhaus import rfg
list_of_filenames = [...]
for filename in list_of_filenames:
    r = rfg.Graph(filename)
    #...process r...
    r.save(filename+".result")
    r.destroy()

7.4.2.4. General Operations

__eq__(other)
__ne__(other)

The two instance methods __eq__ and __ne__ check for equality respectively inequality of two graphs.

Ordinarily, both functions are called by way of the == and != operators, see Listing Equality check.

Graph objects are equal in the sense of these operators if they have been created by the same constructor call. There is no logical checking of graph isomorphisms involved.

Equality check
from bauhaus import rfg
filename = "project.rfg"
rfg1 = rfg.Graph(filename)
rfg2 = rfg.Graph(filename)
rfg3 = rfg1
if rfg1 == rfg2:
    print "Cannot happen because rfg1 and rfg2 have"
    print "not been loaded by the same constructor call"
elif rfg1 == rfg3:
    print "Thats ok, rfg1 and rfg3 are equivalent accesses"
    print "to the same object"

7.4.2.5. Node and Edge Types

Nodes and edges of the RFG are typed. These types are useful for matching purposes. The following operations are related to types:

node_type_names()
is_node_type_name(type_name)
node_type(type_name)
create_node_type(type_name[, parent_type])
reparent_node_type(node_type, parent_type)
destroy_node_type(node_type)

Method node_type_names returns a list of all node types (as strings). You can check whether a string represents a node type by calling is_node_type_name. A string can be translated into a node type by node_type. If you need a new type of node, use create_node_type and supply a name for the new type (which must not be used as name of a node type before) and an optional parent type (identified by its type descriptor). Method destroy_node_type removes a node type from the schema. It throws exception TypeHasInstances if there are still nodes of the node type to be removed. It throws TypeHasSubtype if there are still node types referring to this type.

Method reparent_node_type changes the parent type of a node type.

Caution

Be careful not to introduce cycles in the inheritance hierarchy. This is not checked by the API because it may be necessary to introduce transient cyclic dependencies during mass changes to the hierarchy. Just make sure you end up with a valid hierarchy.

You find a list of all node and edge types related to source code representation in Language Schema. You may also use Gravis in order to get an overview of existing node and edge types: Load an RFG file and the Node types and Edge types palette windows show tree views of all possible node and edge types along with their icons and colors.

edge_type_names()
is_edge_type_name(type_name)
edge_type(type_name)
create_edge_type(type_name, parent_type)
reparent_edge_type(edge_type, parent_type)
destroy_edge_type(edge_type)

The same operations as for nodes types are permitted for edge types as well.

7.4.2.6. New Elements

node(descr)
edge(descr)

The two instance methods node and edge create a new node/a new edge of type descr. You can either choose to pass a descriptor of the type or the name of the type by string. The new element is not contained in any view of the graph but already part of the graph. You have to insert it into a view by means of instance method add of class View.

Caution

A node/an edge has to be inserted in at least one view of a graph before one can set values of attributes at that node/edge.

7.4.2.7. Attributes of Graphs, Views, Nodes, and Edges

For a full and in depth explanation of the attribute handling, see Section Attributes.

Schema Handling for Attributes

The following set of operations can be used for manipulating the schema information about attributes:

graph_attribute_names()
graph_attribute(attr_name)
is_graph_attribute_name(attr_name)
create_graph_attribute(new_attr_name, attr_type)
rename_graph_attribute(attr_name, new_name)
destroy_graph_attribute(attr_name)
view_attribute_names()
view_attribute(attr_name)
is_view_attribute_name(attr_name)
create_view_attribute(new_attr_name, attr_type)
rename_view_attribute(attr_name, new_name)
destroy_view_attribute(attr_name)
node_attribute_names()
node_attribute(attr_name)
is_node_attribute_name(attr_name)
create_node_attribute(new_attr_name, attr_type)
rename_node_attribute(attr_name, new_name)
destroy_node_attribute(attr_name)
edge_attribute_names()
edge_attribute(attr_name)
is_edge_attribute_name(attr_name)
create_edge_attribute(new_attr_name, attr_type)
rename_edge_attribute(attr_name, new_name)
destroy_edge_attribute(attr_name)

Method graph_attribute_names returns a list of all graph attribute names (as strings).

The method graph_attribute converts a string into an attribute descriptor.

If a string denotes a valid graph attribute can be queried by method is_graph_attribute_name.

A new graph attribute can be created using create_graph_attribute. The second parameter specifies the type used for the values stored later on.

An attribute can be renamed by rename_graph_attribute.

If you do not need an attribute anymore, you can remove it from the graph by destroy_graph_attribute.

The other methods analogously work for view, node, and edge attributes. The topic “attributes” is covered in detail in Section Attributes.

Value Handling for Attributes

For a full and in depth explanation of the attribute handling, see Section Attributes.

has_key(key)
__getitem__(key)
__setitem__(key, value)
__delitem__(key)
__iter__()
next()
keys()
items()
get_string_list(key)
set_string_list(key, value)
append_to_string_list(key, value)
These instance methods can be used to query and manipulate the attribute values at a graph.

has_key checks whether attribute key is set.

__getitem__ gets the stored value for attribute key.

__setitem__ sets a new value for the attribute.

__delitem__ removes an attribute value.

__iter__ and next implement the iterator interface.

keys returns a list of the names of all set attributes.

items returns the list of all set pairs of keys (as strings) and values.

get_string_list interprets the string attribute as list and returns the list.

set_string_list stores a string representing the given string list.

append_to_string_list appends the given string to the string list stored in the string attribute.

7.4.2.8. Membership in the Graph

__contains__(element)

Instance method __contains__ checks whether the element passed as parameter (a view, a node or an edge) is contained in the graph or the attribute is set in the graph. The attribute can be either specified by a graph attribute descriptor or by a string which denotes the name of the graph attribute.

Instance method __contains__ is also usable as in operator.

Caution

__contains__ simply returns ’False’ instead of raising an exception in case an attribute specified by name is not a valid graph attribute.

7.4.2.9. Access to Views

A view is a subgraph of an RFG.

view_names()
views()
view(view_name)
is_view_name(view_name)

Instance method view_names returns a list of names of all views in the graph (see Listing Printing the view names of an RFG). Instance method views returns a list of all views. When you know only the name of a view, you get access to the view by supplying view with the name. You can check a name for validity by is_view_name.

Printing the view names of an RFG
graph = rfg.Graph("...")
for view_name in graph.view_names():
    print view_name
nodes([view[, node_matcher]])
edges([view[, edge_matcher]])

These instance methods return all elements (nodes or edges, respectively) in the view as a set. If you supply a matcher function that takes a Node ( Edge respectively) only those elements are supplied for which the matcher holds, see Listing Get the routine nodes contained in a view.

Note

As nodes and edges return a full-fledged set of elements, it is a good idea to use xnodes and xedges when iterating because of memory management reasons.

Get the routine nodes contained in a view
from bauhaus import rfg
my_graph = rfg.Graph(filename)
my_view = my_graph.view(rfg.code_facts_view_name())
routine_type = my_graph.node_type("Routine")

def only_routines(node):
    return node.is_of_subtype(routine_type)

all_routines_in_view = my_graph.nodes(my_view, only_routines)

7.4.2.10. Iterating the Elements Contained in a Graph

xviews()
xnodes([view[, node_matcher]])
xedges([view[, edge_matcher]])

Instance methods xviews, xnodes, and xedges return iterator objects for all views, nodes, and edges contained in the graph. The iterator is of type GraphViewIter, GraphNodeIter/ViewNodeIter, or GraphEdgeIter/ViewEdgeIter, respectively. The view iterators are returned if a view is passed in.

The example in Listing Print the names of all views of an RFG prints the names of all views contained in an RFG.

Print the names of all views of an RFG
from bauhaus import rfg
my_graph = rfg.Graph(filename)
for v in my_graph.xviews():
    print v.name()

If you supply a matcher function that takes a Node ( Edge respectively) only those elements are supplied for which the matcher holds.

Note

These operations have a new parameter view (introduced in 5.7.0) which has not been present in older versions. Either add a named parameter for the matcher function or a value ’None’ as first parameter to existing calls.