summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Schubert <contact@benschubert.me>2019-05-03 21:42:13 +0100
committerBenjamin Schubert <ben.c.schubert@gmail.com>2019-05-04 09:47:19 +0100
commit4c2429145bc1f1929c1f546e04d2cabd14365381 (patch)
tree35012bfb0ccc87b453b25e907fde87b302877bee
parentfbb32cfb91c4a6af52390e87dd16c56934837282 (diff)
downloadbuildstream-4c2429145bc1f1929c1f546e04d2cabd14365381.tar.gz
WIP: _yaml: reimplement using cython for performance
-rw-r--r--buildstream/_yaml.pyx (renamed from buildstream/_yaml.py)311
-rwxr-xr-xsetup.py3
2 files changed, 170 insertions, 144 deletions
diff --git a/buildstream/_yaml.py b/buildstream/_yaml.pyx
index cdab4269e..38947d595 100644
--- a/buildstream/_yaml.py
+++ b/buildstream/_yaml.pyx
@@ -61,16 +61,32 @@ from ._exceptions import LoadError, LoadErrorReason
# line = Node[2]
# column = Node[3]
#
-class Node(namedtuple('Node', ['value', 'file_index', 'line', 'column'])):
+cdef class Node:
+ cdef public object value
+ # FIXME: we want a int here
+ cdef public object file_index
+ cdef public int line
+ cdef public int column
+
+ def __cinit__(self, object value, object file_index, int line, int column):
+ self.value = value
+ self.file_index = file_index
+ self.line = line
+ self.column = column
+
def __contains__(self, what):
- # Delegate to the inner value, though this will likely not work
- # very well if the node is a list or string, it's unlikely that
- # code which has access to such nodes would do this.
- return what in self[0]
+ return what in self.value
+
+# class Node(namedtuple('Node', ['value', 'file_index', 'line', 'column'])):
+# def __contains__(self, what):
+# # Delegate to the inner value, though this will likely not work
+# # very well if the node is a list or string, it's unlikely that
+# # code which has access to such nodes would do this.
+# return what in self[0]
# File name handling
-_FILE_LIST = []
+cdef list _FILE_LIST = []
# Purely synthetic node will have None for the file number, have line number
@@ -98,7 +114,7 @@ class ProvenanceInformation:
def __init__(self, nodeish):
self.node = nodeish
- if (nodeish is None) or (nodeish[1] is None):
+ if (nodeish is None) or (nodeish.file_index is None):
self.filename = ""
self.shortname = ""
self.displayname = ""
@@ -107,13 +123,13 @@ class ProvenanceInformation:
self.toplevel = None
self.project = None
else:
- fileinfo = _FILE_LIST[nodeish[1]]
+ fileinfo = _FILE_LIST[nodeish.file_index]
self.filename = fileinfo[0]
self.shortname = fileinfo[1]
self.displayname = fileinfo[2]
# We add 1 here to convert from computerish to humanish
- self.line = nodeish[2] + 1
- self.col = nodeish[3]
+ self.line = nodeish.line + 1
+ self.col = nodeish.column
self.toplevel = fileinfo[3]
self.project = fileinfo[4]
self.is_synthetic = (self.filename == '') or (self.col < 0)
@@ -148,13 +164,11 @@ class YAMLLoadError(Exception):
# Mappings must only have string keys, values are always represented as
# strings if they are scalar, or else as simple dictionaries and lists.
#
-class Representer:
- __slots__ = (
- "_file_index",
- "state",
- "output",
- "keys",
- )
+cdef class Representer:
+ cdef object _file_index # FIXME: this should be an int
+ cdef str state
+ cdef list output
+ cdef list keys
# Initialise a new representer
#
@@ -163,7 +177,7 @@ class Representer:
#
# Args:
# file_index (int): The index of this YAML file
- def __init__(self, file_index):
+ def __cinit__(self, object file_index):
self._file_index = file_index
self.state = "init"
self.output = []
@@ -226,14 +240,14 @@ class Representer:
def _handle_wait_value_ScalarEvent(self, ev):
key = self.keys.pop()
- self.output[-1][0][key] = \
+ self.output[-1].value[key] = \
Node(ev.value, self._file_index, ev.start_mark.line, ev.start_mark.column)
return "wait_key"
def _handle_wait_value_MappingStartEvent(self, ev):
new_state = self._handle_doc_MappingStartEvent(ev)
key = self.keys.pop()
- self.output[-2][0][key] = self.output[-1]
+ self.output[-2].value[key] = self.output[-1]
return new_state
def _handle_wait_key_MappingEndEvent(self, ev):
@@ -241,7 +255,7 @@ class Representer:
# unless it's the last one in which case we leave it
if len(self.output) > 1:
self.output.pop()
- if type(self.output[-1][0]) is list:
+ if type(self.output[-1].value) is list:
return "wait_list_item"
else:
return "wait_key"
@@ -250,13 +264,13 @@ class Representer:
def _handle_wait_value_SequenceStartEvent(self, ev):
self.output.append(Node([], self._file_index, ev.start_mark.line, ev.start_mark.column))
- self.output[-2][0][self.keys[-1]] = self.output[-1]
+ self.output[-2].value[self.keys[-1]] = self.output[-1]
return "wait_list_item"
def _handle_wait_list_item_SequenceStartEvent(self, ev):
- self.keys.append(len(self.output[-1][0]))
+ self.keys.append(len(self.output[-1].value))
self.output.append(Node([], self._file_index, ev.start_mark.line, ev.start_mark.column))
- self.output[-2][0].append(self.output[-1])
+ self.output[-2].value.append(self.output[-1])
return "wait_list_item"
def _handle_wait_list_item_SequenceEndEvent(self, ev):
@@ -271,13 +285,13 @@ class Representer:
return "wait_key"
def _handle_wait_list_item_ScalarEvent(self, ev):
- self.output[-1][0].append(
+ self.output[-1].value.append(
Node(ev.value, self._file_index, ev.start_mark.line, ev.start_mark.column))
return "wait_list_item"
def _handle_wait_list_item_MappingStartEvent(self, ev):
new_state = self._handle_doc_MappingStartEvent(ev)
- self.output[-2][0].append(self.output[-1])
+ self.output[-2].value.append(self.output[-1])
return new_state
def _handle_doc_DocumentEndEvent(self, ev):
@@ -332,6 +346,9 @@ def load(filename, shortname=None, copy_tree=False, *, project=None):
"{} is a directory. bst command expects a .bst file."
.format(filename)) from e
except LoadError as e:
+ print("ERROR HERE", e)
+ import traceback
+ traceback.print_exc()
raise LoadError(e.reason, "{}: {}".format(displayname, e)) from e
@@ -351,14 +368,15 @@ def load_data(data, file_index=None, file_name=None, copy_tree=False):
raise LoadError(LoadErrorReason.INVALID_YAML,
"Severely malformed YAML:\n\n{}\n\n".format(e)) from e
- if not isinstance(contents, tuple) or not isinstance(contents[0], dict):
+ # FIXME: we can probably remove the instance check
+ if not isinstance(contents, Node) or not isinstance(contents.value, dict):
# Special case allowance for None, when the loaded file has only comments in it.
if contents is None:
contents = Node({}, file_index, 0, 0)
else:
raise LoadError(LoadErrorReason.INVALID_YAML,
"YAML file has content of type '{}' instead of expected type 'dict': {}"
- .format(type(contents[0]).__name__, file_name))
+ .format(type(contents.value).__name__, file_name))
# Store this away because we'll use it later for "top level" provenance
if file_index is not None:
@@ -409,11 +427,11 @@ def node_get_provenance(node, key=None, indices=None):
return ProvenanceInformation(node)
if key and not indices:
- return ProvenanceInformation(node[0].get(key))
+ return ProvenanceInformation(node.value.get(key))
- nodeish = node[0].get(key)
+ nodeish = node.value.get(key)
for idx in indices:
- nodeish = nodeish[0][idx]
+ nodeish = nodeish.value[idx]
return ProvenanceInformation(nodeish)
@@ -451,11 +469,11 @@ def node_get(node, expected_type, key, indices=None, *, default_value=_sentinel,
if indices is None:
if default_value is _sentinel:
- value = node[0].get(key, Node(default_value, None, 0, 0))
+ value = node.value.get(key, Node(default_value, None, 0, 0))
else:
- value = node[0].get(key, Node(default_value, None, 0, next(_SYNTHETIC_COUNTER)))
+ value = node.value.get(key, Node(default_value, None, 0, next(_SYNTHETIC_COUNTER)))
- if value[0] is _sentinel:
+ if value.value is _sentinel:
provenance = node_get_provenance(node)
raise LoadError(LoadErrorReason.INVALID_DATA,
"{}: Dictionary did not contain expected key '{}'".format(provenance, key))
@@ -464,35 +482,36 @@ def node_get(node, expected_type, key, indices=None, *, default_value=_sentinel,
# No need to synthesise useful node content as we destructure it immediately
value = Node(node_get(node, list, key), None, 0, 0)
for index in indices:
- value = value[0][index]
+ value = value.value[index]
if type(value) is not Node:
- value = (value,)
+ value = Node(value, None, 0, 0)
# Optionally allow None as a valid value for any type
- if value[0] is None and (allow_none or default_value is None):
+ if value.value is None and (allow_none or default_value is None):
return None
- if (expected_type is not None) and (not isinstance(value[0], expected_type)):
+ if (expected_type is not None) and (not isinstance(value.value, expected_type)):
# Attempt basic conversions if possible, typically we want to
# be able to specify numeric values and convert them to strings,
# but we dont want to try converting dicts/lists
try:
- if (expected_type == bool and isinstance(value[0], str)):
+ if (expected_type == bool and isinstance(value.value, str)):
# Dont coerce booleans to string, this makes "False" strings evaluate to True
# We don't structure into full nodes since there's no need.
- if value[0] in ('True', 'true'):
- value = (True, None, 0, 0)
- elif value[0] in ('False', 'false'):
- value = (False, None, 0, 0)
+ if value.value in ('True', 'true'):
+ value = Node(True, None, 0, 0)
+ elif value.value in ('False', 'false'):
+ value = Node(False, None, 0, 0)
else:
raise ValueError()
elif not (expected_type == list or
expected_type == dict or
- isinstance(value[0], (list, dict))):
- value = (expected_type(value[0]), None, 0, 0)
+ isinstance(value.value, (list, dict))):
+ value = Node(expected_type(value.value), None, 0, 0)
else:
raise ValueError()
- except (ValueError, TypeError):
+ except (ValueError, TypeError) as e:
+ print(e)
provenance = node_get_provenance(node, key=key, indices=indices)
if indices:
path = [key]
@@ -505,8 +524,8 @@ def node_get(node, expected_type, key, indices=None, *, default_value=_sentinel,
.format(provenance, path, expected_type.__name__))
# Now collapse lists, and scalars, to their value, leaving nodes as-is
- if type(value[0]) is not dict:
- value = value[0]
+ if type(value.value) is not dict:
+ value = value.value
# Trim it at the bud, let all loaded strings from yaml be stripped of whitespace
if type(value) is str:
@@ -520,17 +539,17 @@ def node_get(node, expected_type, key, indices=None, *, default_value=_sentinel,
return value
-def __trim_list_provenance(value):
+cdef list __trim_list_provenance(list value):
ret = []
for entry in value:
if type(entry) is not Node:
- entry = (entry, None, 0, 0)
- if type(entry[0]) is list:
- ret.append(__trim_list_provenance(entry[0]))
- elif type(entry[0]) is dict:
+ entry = Node(entry, None, 0, 0)
+ if type(entry.value) is list:
+ ret.append(__trim_list_provenance(entry.value))
+ elif type(entry.value) is dict:
ret.append(entry)
else:
- ret.append(entry[0])
+ ret.append(entry.value)
return ret
@@ -541,30 +560,30 @@ def __trim_list_provenance(value):
# create entries before using `node_set`
#
# Args:
-# node (tuple): The node
+# node (Node): The node
# key (str): The key name
# value: The value
# indices: Any indices to index into the list referenced by key, like in
# `node_get` (must be a list of integers)
#
-def node_set(node, key, value, indices=None):
+def node_set(Node node, str key, value, list indices=None):
if indices:
- node = node[0][key]
+ node = node.value[key]
key = indices.pop()
for idx in indices:
node = node[0][idx]
if type(value) is Node:
- node[0][key] = value
+ node.value[key] = value
else:
try:
# Need to do this just in case we're modifying a list
- old_value = node[0][key]
+ old_value = node.value[key]
except KeyError:
old_value = None
if old_value is None:
- node[0][key] = Node(value, node[1], node[2], next(_SYNTHETIC_COUNTER))
+ node.value[key] = Node(value, node.file_index, node.line, next(_SYNTHETIC_COUNTER))
else:
- node[0][key] = Node(value, old_value[1], old_value[2], old_value[3])
+ node.value[key] = Node(value, old_value.file_index, old_value.line, old_value.column)
# node_extend_list()
@@ -585,20 +604,20 @@ def node_set(node, key, value, indices=None):
def node_extend_list(node, key, length, default):
assert type(default) is str or default in ([], {})
- list_node = node[0].get(key)
+ list_node = node.value.get(key)
if list_node is None:
list_node = node[0][key] = Node([], node[1], node[2], next(_SYNTHETIC_COUNTER))
- assert type(list_node[0]) is list
+ assert type(list_node.value) is list
- the_list = list_node[0]
+ the_list = list_node.value
def_type = type(default)
- file_index = node[1]
+ file_index = node.file_index
if the_list:
line_num = the_list[-1][2]
else:
- line_num = list_node[2]
+ line_num = list_node.line
while length > len(the_list):
if def_type is str:
@@ -628,15 +647,15 @@ def node_extend_list(node, key, length, default):
def node_items(node):
if type(node) is not Node:
node = Node(node, None, 0, 0)
- for key, value in node[0].items():
+ for key, value in node.value.items():
if type(value) is not Node:
value = Node(value, None, 0, 0)
- if type(value[0]) is dict:
+ if type(value.value) is dict:
yield (key, value)
- elif type(value[0]) is list:
- yield (key, __trim_list_provenance(value[0]))
+ elif type(value.value) is list:
+ yield (key, __trim_list_provenance(value.value))
else:
- yield (key, value[0])
+ yield (key, value.value)
# node_keys()
@@ -653,7 +672,7 @@ def node_items(node):
def node_keys(node):
if type(node) is not Node:
node = Node(node, None, 0, 0)
- yield from node[0].keys()
+ yield from node.value.keys()
# node_del()
@@ -668,7 +687,7 @@ def node_keys(node):
#
def node_del(node, key, safe=False):
try:
- del node[0][key]
+ del node.value[key]
except KeyError:
if not safe:
raise
@@ -689,7 +708,7 @@ def node_del(node, key, safe=False):
def is_node(maybenode):
# It's a programming error to give this a Node which isn't a mapping
# so assert that.
- assert (type(maybenode) is not Node) or (type(maybenode[0]) is dict)
+ assert (type(maybenode) is not Node) or (type(maybenode.value) is dict)
# Now return the type check
return type(maybenode) is Node
@@ -729,7 +748,7 @@ def new_synthetic_file(filename, project=None):
#
def new_empty_node(ref_node=None):
if ref_node is not None:
- return Node({}, ref_node[1], ref_node[2], next(_SYNTHETIC_COUNTER))
+ return Node({}, ref_node.file_index, ref_node.line, next(_SYNTHETIC_COUNTER))
else:
return Node({}, None, 0, 0)
@@ -785,9 +804,9 @@ def __new_node_from_list(inlist):
# (LoadError): If node was a mapping and contained a mix of
# list composition directives and other keys
#
-def _is_composite_list(node):
+cdef bint _is_composite_list(Node node):
- if type(node[0]) is dict:
+ if type(node.value) is dict:
has_directives = False
has_keys = False
@@ -857,18 +876,18 @@ def _compose_composite_list(target, source):
# target (Node): The target list to be composed into
# source (Node): The composition list to be composed from
#
-def _compose_list(target, source):
- clobber = source[0].get("(=)")
- prefix = source[0].get("(<)")
- suffix = source[0].get("(>)")
+def _compose_list(Node target, Node source): # FIXME: make private
+ clobber = source.value.get("(=)")
+ prefix = source.value.get("(<)")
+ suffix = source.value.get("(>)")
if clobber is not None:
- target[0].clear()
- target[0].extend(clobber[0])
+ target.value.clear()
+ target.value.extend(clobber.value)
if prefix is not None:
- for v in reversed(prefix[0]):
- target[0].insert(0, v)
+ for v in reversed(prefix.value):
+ target.value.insert(0, v)
if suffix is not None:
- target[0].extend(suffix[0])
+ target.value.extend(suffix.value)
# composite_dict()
@@ -882,16 +901,17 @@ def _compose_list(target, source):
#
# Raises: CompositeError
#
-def composite_dict(target, source, path=None):
+def composite_dict(Node target, Node source, list path=None):
if path is None:
path = []
- for k, v in source[0].items():
+ # FIXME: type k and v
+ for k, v in source.value.items():
path.append(k)
- if type(v[0]) is list:
+ if type(v.value) is list:
# List clobbers anything list-like
- target_value = target[0].get(k)
+ target_value = target.value.get(k)
if not (target_value is None or
- type(target_value[0]) is list or
+ type(target_value.value) is list or
_is_composite_list(target_value)):
raise CompositeError(path,
"{}: List cannot overwrite {} at: {}"
@@ -899,51 +919,51 @@ def composite_dict(target, source, path=None):
k,
node_get_provenance(target, k)))
# Looks good, clobber it
- target[0][k] = v
+ target.value[k] = v
elif _is_composite_list(v):
- if k not in target[0]:
+ if k not in target.value:
# Composite list clobbers empty space
- target[0][k] = v
- elif type(target[0][k][0]) is list:
+ target.value[k] = v
+ elif type(target.value[k].value) is list:
# Composite list composes into a list
- _compose_list(target[0][k], v)
- elif _is_composite_list(target[0][k]):
+ _compose_list(target.value[k], v)
+ elif _is_composite_list(target.value[k]):
# Composite list merges into composite list
- _compose_composite_list(target[0][k], v)
+ _compose_composite_list(target.value[k], v)
else:
# Else composing on top of normal dict or a scalar, so raise...
raise CompositeError(path,
"{}: Cannot compose lists onto {}".format(
node_get_provenance(v),
- node_get_provenance(target[0][k])))
- elif type(v[0]) is dict:
+ node_get_provenance(target.value[k])))
+ elif type(v.value) is dict:
# We're composing a dict into target now
- if k not in target[0]:
+ if k not in target.value:
# Target lacks a dict at that point, make a fresh one with
# the same provenance as the incoming dict
- target[0][k] = Node({}, v[1], v[2], v[3])
- if type(target[0]) is not dict:
+ target.value[k] = Node({}, v.file_index, v.line, v.column)
+ if type(target.value) is not dict:
raise CompositeError(path,
"{}: Cannot compose dictionary onto {}".format(
node_get_provenance(v),
- node_get_provenance(target[0][k])))
- composite_dict(target[0][k], v, path)
+ node_get_provenance(target.value[k])))
+ composite_dict(target.value[k], v, path)
else:
- target_value = target[0].get(k)
- if target_value is not None and type(target_value[0]) is not str:
+ target_value = target.value.get(k)
+ if target_value is not None and type(target_value.value) is not str:
raise CompositeError(path,
"{}: Cannot compose scalar on non-scalar at {}".format(
node_get_provenance(v),
- node_get_provenance(target[0][k])))
- target[0][k] = v
+ node_get_provenance(target.value[k])))
+ target.value[k] = v
path.pop()
# Like composite_dict(), but raises an all purpose LoadError for convenience
#
def composite(target, source):
- assert type(source[0]) is dict
- assert type(target[0]) is dict
+ assert type(source.value) is dict
+ assert type(target.value) is dict
try:
composite_dict(target, source)
@@ -961,12 +981,13 @@ def composite(target, source):
# Like composite(target, source), but where target overrides source instead.
#
+# FIXME: private?
def composite_and_move(target, source):
composite(source, target)
- to_delete = [key for key in target[0].keys() if key not in source[0]]
- for key, value in source[0].items():
- target[0][key] = value
+ to_delete = [key for key in target.value.keys() if key not in source.value]
+ for key, value in source.value.items():
+ target.value[key] = value
for key in to_delete:
del target[0][key]
@@ -987,7 +1008,7 @@ def node_sanitize(node, *, dict_type=OrderedDict):
# If we have an unwrappable node, unwrap it
if node_type is Node:
- node = node[0]
+ node = node.value
node_type = type(node)
# Short-circuit None which occurs ca. twice per element
@@ -1039,7 +1060,7 @@ def node_validate(node, valid_keys):
# Probably the fastest way to do this: https://stackoverflow.com/a/23062482
valid_keys = set(valid_keys)
- invalid = next((key for key in node[0] if key not in valid_keys), None)
+ invalid = next((key for key in node.value if key not in valid_keys), None)
if invalid:
provenance = node_get_provenance(node, key=invalid)
@@ -1075,10 +1096,10 @@ __NODE_ASSERT_COMPOSITION_DIRECTIVES = ('(>)', '(<)', '(=)')
# Returns:
# (Node): A deep copy of source with provenance preserved.
#
-def node_copy(source):
+def node_copy(Node source):
copy = {}
- for key, value in source[0].items():
- value_type = type(value[0])
+ for key, value in source.value.items():
+ value_type = type(value.value)
if value_type is dict:
copy[key] = node_copy(value)
elif value_type is list:
@@ -1088,14 +1109,18 @@ def node_copy(source):
else:
raise ValueError("Unable to be quick about node_copy of {}".format(value_type))
- return Node(copy, source[1], source[2], source[3])
+ return Node(copy, source.file_index, source.line, source.column)
# Internal function to help node_copy() but for lists.
-def _list_copy(source):
+cdef Node _list_copy(Node source):
copy = []
- for item in source[0]:
- item_type = type(item[0])
+ for item in source.value:
+ try:
+ item_type = type(item.value)
+ except AttributeError: # FIXME: this seems... wrong
+ item_type = type(item[0])
+
if item_type is dict:
copy.append(node_copy(item))
elif item_type is list:
@@ -1105,7 +1130,7 @@ def _list_copy(source):
else:
raise ValueError("Unable to be quick about list_copy of {}".format(item_type))
- return Node(copy, source[1], source[2], source[3])
+ return Node(copy, source.file_index, source.line, source.column)
# node_final_assertions()
@@ -1119,10 +1144,10 @@ def _list_copy(source):
# Raises:
# (LoadError): If any assertions fail
#
-def node_final_assertions(node):
- assert type(node) is Node
+def node_final_assertions(Node node):
+ # assert type(node) is Node
- for key, value in node[0].items():
+ for key, value in node.value.items():
# Assert that list composition directives dont remain, this
# indicates that the user intended to override a list which
@@ -1133,7 +1158,7 @@ def node_final_assertions(node):
raise LoadError(LoadErrorReason.TRAILING_LIST_DIRECTIVE,
"{}: Attempt to override non-existing list".format(provenance))
- value_type = type(value[0])
+ value_type = type(value.value)
if value_type is dict:
node_final_assertions(value)
@@ -1142,9 +1167,9 @@ def node_final_assertions(node):
# Helper function for node_final_assertions(), but for lists.
-def _list_final_assertions(values):
- for value in values[0]:
- value_type = type(value[0])
+def _list_final_assertions(Node values):
+ for value in values.value:
+ value_type = type(value.value)
if value_type is dict:
node_final_assertions(value)
@@ -1214,11 +1239,11 @@ def assert_symbol_name(provenance, symbol_name, purpose, *, allow_dashes=True):
#
# Returns:
# (list): A path from `node` to `target` or None if `target` is not in the subtree
-def node_find_target(node, target, *, key=None):
- assert type(node) is Node
- assert type(target) is Node
+def node_find_target(Node node, Node target, *, str key=None):
+ assert type(node) is Node # FIXME: remove this assert
+ assert type(target) is Node # FIXME: remove this assert
if key is not None:
- target = target[0][key]
+ target = target.value[key]
path = []
if _walk_find_target(node, path, target):
@@ -1230,19 +1255,19 @@ def node_find_target(node, target, *, key=None):
# Helper for node_find_target() which walks a value
-def _walk_find_target(node, path, target):
- if node[1:] == target[1:]:
+def _walk_find_target(Node node, list path, Node target):
+ if node.file_index == target.file_index and node.line == target.line and node.column == target.column:
return True
- elif type(node[0]) is dict:
+ elif type(node.value) is dict:
return _walk_dict_node(node, path, target)
- elif type(node[0]) is list:
+ elif type(node.value) is list:
return _walk_list_node(node, path, target)
return False
# Helper for node_find_target() which walks a list
-def _walk_list_node(node, path, target):
- for i, v in enumerate(node[0]):
+def _walk_list_node(Node node, list path, Node target):
+ for i, v in enumerate(node.value):
path.append(i)
if _walk_find_target(v, path, target):
return True
@@ -1251,8 +1276,8 @@ def _walk_list_node(node, path, target):
# Helper for node_find_target() which walks a mapping
-def _walk_dict_node(node, path, target):
- for k, v in node[0].items():
+def _walk_dict_node(Node node, list path, Node target):
+ for k, v in node.value.items():
path.append(k)
if _walk_find_target(v, path, target):
return True
diff --git a/setup.py b/setup.py
index 3ee43d44d..443791017 100755
--- a/setup.py
+++ b/setup.py
@@ -367,7 +367,8 @@ setup(name='BuildStream',
],
ext_modules=cythonize(
[
- "buildstream/_variables.pyx"
+ "buildstream/_variables.pyx",
+ "buildstream/_yaml.pyx",
],
annotate=True,
language_level="3",