Using GCL files from Python¶
You now know enough GCL to get started. Using the library looks like this:
import gcl
from gcl import util
# Load and evaluate the given file
model = gcl.load('myfile.gcl')
# This gives you a dict-like object back, that you can just index
print(model['element'])
# Translate the whole thing to a Python dict (for example to convert to JSON)
dict_model = util.to_python(model)
import json
print(json.dumps(dict_model))
to_python
will respect the visibility of keys as specified in the schema.
Bindings variables from the script¶
Add bindings to the top-level scope from the evaluating script by passing a dictionary with bindings
to the load
function:
gcl.load('file.gcl', env={'swallow': 'unladen'})
This is also how you add custom functions to the model.
Finding nodes by location: GPath¶
Sometimes you want to select specific values out of a big model. For that purpose, you can use GPath, which is a hierarchical selector language to select one or more values from a model.
GPath queries look like this:
# Select node by name
name.name.name
# Select multiple nodes
name.{name1,name2}.name
# Select all nodes at a given level
name.*.name
# List indices are numbers in the path between square brackets
name.[0].name
The command-line tools use GPath for selecting nodes from the model. Using GPath in your own script looks like this:
import gcl
from gcl import query
model = gcl.load('python.gcl')
q = query.GPath([
'*.favorite_color',
'lancelot',
])
results = q.select(model)
# A list of all values found
print(results.values())
# A list of (path, value) tuples of all values found
print(results.paths_values())
# A deep copy of all selected values into a dict
print(results.deep())
Finding nodes by criteria: TupleFinder¶
For some reason, I keep on using GCL in scripts where I have big collection of “things”, and I want to do an action to each particular thing. Since this is a common usage pattern, there are some standard classes to help with that.
You have option to search the entire model for all tuples that have a particular
key (HasKeyCondition
, and another useful pattern is to do an additional level
of dispatch on the value of that key), or for all tuples (or elements) that are
in lists with particular key names anywhere in the model (InListCondition
).
If the found tuples contain references to one another, the TupleFinder
even
has the ability to order tuples by dependency, so that tuples that are depended
upon come earlier in the list than the tuples that depend on them (unless there
is a cyclic reference).
import gcl
from gcl import query
obj = gcl.load(model.gcl')
finder = query.TupleFinder(query.HasKeyCondition('type', search_lists=True))
finder.find(obj)
# All matched objects are now in finder.unordered
print(finder.unordered))
# We'll do an ordering based on dependencies
finder.order()
if finder.has_recursive_dependency():
print('Some nodes have a recursive dependency!')
print(finder.find_recursive_dependency())
else:
# Nodes got moved to finder.ordered
print(finder.ordered)
Thread safety¶
Currently, GCL evaluation is not thread safe. Unfortunately we need to store some global state to track evaluations so we can detect infinite recursion inside the model.
Let me know if this poses a problem for you, we can make it into threadlocals without much issue.