diff options
Diffstat (limited to 'src/buildstream/_yaml.pyx')
-rw-r--r-- | src/buildstream/_yaml.pyx | 880 |
1 files changed, 1 insertions, 879 deletions
diff --git a/src/buildstream/_yaml.pyx b/src/buildstream/_yaml.pyx index 80c7a59ce..11b060b4d 100644 --- a/src/buildstream/_yaml.pyx +++ b/src/buildstream/_yaml.pyx @@ -31,832 +31,12 @@ from collections.abc import Mapping from ruamel import yaml from ._exceptions import LoadError, LoadErrorReason - - -# Without this, pylint complains about all the `type(foo) is blah` checks -# because it feels isinstance() is more idiomatic. Sadly, it is much slower to -# do `isinstance(foo, blah)` for reasons I am unable to fathom. As such, we -# blanket disable the check for this module. -# -# pylint: disable=unidiomatic-typecheck - - -# A sentinel to be used as a default argument for functions that need -# to distinguish between a kwarg set to None and an unset kwarg. -_sentinel = object() - - -# Node() -# -# Container for YAML loaded data and its provenance -# -# All nodes returned (and all internal lists/strings) have this type (rather -# than a plain tuple, to distinguish them in things like node_sanitize) -# -# Members: -# file_index (int): Index within _FILE_LIST (a list of loaded file paths). -# Negative indices indicate synthetic nodes so that -# they can be referenced. -# line (int): The line number within the file where the value appears. -# col (int): The column number within the file where the value appears. -# -cdef class Node: - - def __init__(self): - raise NotImplementedError("Please do not construct nodes like this. Use Node.__new__(Node, *args) instead.") - - def __cinit__(self, int file_index, int line, int column, *args): - self.file_index = file_index - self.line = line - self.column = column - - def __json__(self): - raise ValueError("Nodes should not be allowed when jsonify-ing data", self) - - ############################################################# - # Public Methods # - ############################################################# - - @classmethod - def from_dict(cls, dict value): - if value: - return _new_node_from_dict(value, MappingNode.__new__( - MappingNode, _SYNTHETIC_FILE_INDEX, 0, next_synthetic_counter(), {})) - else: - # We got an empty dict, we can shortcut - return MappingNode.__new__(MappingNode, _SYNTHETIC_FILE_INDEX, 0, next_synthetic_counter(), {}) - - cpdef Node copy(self): - raise NotImplementedError() - - cpdef ProvenanceInformation get_provenance(self): - return ProvenanceInformation(self) - - cpdef object strip_node_info(self): - raise NotImplementedError() - - ############################################################# - # Private Methods used in BuildStream # - ############################################################# - - # _assert_fully_composited() - # - # This must be called on a fully loaded and composited node, - # after all composition has completed. - # - # This checks that no more composition directives are present - # in the data. - # - # Raises: - # (LoadError): If any assertions fail - # - cpdef void _assert_fully_composited(self) except *: - raise NotImplementedError() - - ############################################################# - # Module Local Methods # - ############################################################# - - cdef void _compose_on(self, str key, MappingNode target, list path) except *: - raise NotImplementedError() - - # _is_composite_list - # - # Checks if the node is a Mapping with array composition - # directives. - # - # Returns: - # (bool): True if node was a Mapping containing only - # list composition directives - # - # Raises: - # (LoadError): If node was a mapping and contained a mix of - # list composition directives and other keys - # - cdef bint _is_composite_list(self) except *: - raise NotImplementedError() - - cdef bint _shares_position_with(self, Node target): - return self.file_index == target.file_index and self.line == target.line and self.column == target.column - - cdef bint _walk_find(self, Node target, list path) except *: - raise NotImplementedError() - - -cdef class ScalarNode(Node): - - def __cinit__(self, int file_index, int line, int column, object value): - cdef value_type = type(value) - - if value_type is str: - value = value.strip() - elif value_type is bool: - if value: - value = "True" - else: - value = "False" - elif value_type is int: - value = str(value) - elif value is None: - pass - else: - raise ValueError("ScalarNode can only hold str, int, bool or None objects") - - self.value = value - - ############################################################# - # Public Methods # - ############################################################# - - cpdef ScalarNode copy(self): - return self - - cpdef bint as_bool(self) except *: - if type(self.value) is bool: - return self.value - - # Don't coerce booleans to string, this makes "False" strings evaluate to True - if self.value in ('True', 'true'): - return True - elif self.value in ('False', 'false'): - return False - else: - provenance = self.get_provenance() - path = provenance._toplevel._find(self)[-1] - raise LoadError(LoadErrorReason.INVALID_DATA, - "{}: Value of '{}' is not of the expected type '{}'" - .format(provenance, path, bool.__name__, self.value)) - - cpdef int as_int(self) except *: - try: - return int(self.value) - except ValueError: - provenance = self.get_provenance() - path = provenance._toplevel._find(self)[-1] - raise LoadError(LoadErrorReason.INVALID_DATA, - "{}: Value of '{}' is not of the expected type '{}'" - .format(provenance, path, int.__name__)) - - cpdef str as_str(self): - # We keep 'None' as 'None' to simplify the API's usage and allow chaining for users - if self.value is None: - return None - return str(self.value) - - cpdef bint is_none(self): - return self.value is None - - cpdef object strip_node_info(self): - return self.value - - ############################################################# - # Private Methods used in BuildStream # - ############################################################# - - cpdef void _assert_fully_composited(self) except *: - pass - - ############################################################# - # Module Local Methods # - ############################################################# - - cdef void _compose_on(self, str key, MappingNode target, list path) except *: - cdef Node target_value = target.value.get(key) - - if target_value is not None and type(target_value) is not ScalarNode: - raise CompositeError(path, - "{}: Cannot compose scalar on non-scalar at {}".format( - self.get_provenance(), - target_value.get_provenance())) - - target.value[key] = self - - cdef bint _is_composite_list(self) except *: - return False - - cdef bint _walk_find(self, Node target, list path) except *: - return self._shares_position_with(target) - - -cdef class MappingNode(Node): - - def __cinit__(self, int file_index, int line, int column, dict value): - self.value = value - - def __contains__(self, what): - return what in self.value - - def __delitem__(self, str key): - del self.value[key] - - def __setitem__(self, str key, object value): - cdef Node old_value - - if type(value) in [MappingNode, ScalarNode, SequenceNode]: - self.value[key] = value - else: - node = _create_node_recursive(value, self) - - # FIXME: Do we really want to override provenance? - # - # Related to https://gitlab.com/BuildStream/buildstream/issues/1058 - # - # There are only two cases were nodes are set in the code (hence without provenance): - # - When automatic variables are set by the core (e-g: max-jobs) - # - when plugins call Element.set_public_data - # - # The first case should never throw errors, so it is of limited interests. - # - # The second is more important. What should probably be done here is to have 'set_public_data' - # able of creating a fake provenance with the name of the plugin, the project and probably the - # element name. - # - # We would therefore have much better error messages, and would be able to get rid of most synthetic - # nodes. - old_value = self.value.get(key) - if old_value: - node.file_index = old_value.file_index - node.line = old_value.line - node.column = old_value.column - - self.value[key] = node - - ############################################################# - # Public Methods # - ############################################################# - - cpdef MappingNode copy(self): - cdef dict copy = {} - cdef str key - cdef Node value - - for key, value in self.value.items(): - copy[key] = value.copy() - - return MappingNode.__new__(MappingNode, self.file_index, self.line, self.column, copy) - - cpdef int get_int(self, str key, object default=_sentinel) except *: - cdef ScalarNode scalar = self.get_scalar(key, default) - return scalar.as_int() - - cpdef bint get_bool(self, str key, object default=_sentinel) except *: - cdef ScalarNode scalar = self.get_scalar(key, default) - return scalar.as_bool() - - cpdef MappingNode get_mapping(self, str key, object default=_sentinel): - value = self._get(key, default, MappingNode) - - if type(value) is not MappingNode and value is not None: - provenance = value.get_provenance() - raise LoadError(LoadErrorReason.INVALID_DATA, - "{}: Value of '{}' is not of the expected type 'Mapping'" - .format(provenance, key)) - - return value - - cpdef Node get_node(self, str key, list allowed_types = None, bint allow_none = False): - cdef value = self.value.get(key, _sentinel) - - if value is _sentinel: - if allow_none: - return None - - provenance = self.get_provenance() - raise LoadError(LoadErrorReason.INVALID_DATA, - "{}: Dictionary did not contain expected key '{}'".format(provenance, key)) - - if allowed_types and type(value) not in allowed_types: - provenance = self.get_provenance() - raise LoadError(LoadErrorReason.INVALID_DATA, - "{}: Value of '{}' is not one of the following: {}.".format( - provenance, key, ", ".join(allowed_types))) - - return value - - cpdef ScalarNode get_scalar(self, str key, object default=_sentinel): - value = self._get(key, default, ScalarNode) - - if type(value) is not ScalarNode: - if value is None: - value = ScalarNode.__new__(ScalarNode, self.file_index, 0, next_synthetic_counter(), None) - else: - provenance = value.get_provenance() - raise LoadError(LoadErrorReason.INVALID_DATA, - "{}: Value of '{}' is not of the expected type 'Scalar'" - .format(provenance, key)) - - return value - - cpdef SequenceNode get_sequence(self, str key, object default=_sentinel): - value = self._get(key, default, SequenceNode) - - if type(value) is not SequenceNode and value is not None: - provenance = value.get_provenance() - raise LoadError(LoadErrorReason.INVALID_DATA, - "{}: Value of '{}' is not of the expected type 'Sequence'" - .format(provenance, key)) - - return value - - cpdef str get_str(self, str key, object default=_sentinel): - cdef ScalarNode scalar = self.get_scalar(key, default) - return scalar.as_str() - - cpdef object items(self): - return self.value.items() - - cpdef list keys(self): - return list(self.value.keys()) - - cpdef void safe_del(self, str key): - try: - del self.value[key] - except KeyError: - pass - - cpdef object strip_node_info(self): - cdef str key - cdef Node value - - return {key: value.strip_node_info() for key, value in self.value.items()} - - # validate_keys() - # - # Validate the node so as to ensure the user has not specified - # any keys which are unrecognized by buildstream (usually this - # means a typo which would otherwise not trigger an error). - # - # Args: - # valid_keys (list): A list of valid keys for the specified node - # - # Raises: - # LoadError: In the case that the specified node contained - # one or more invalid keys - # - cpdef void validate_keys(self, list valid_keys) except *: - # Probably the fastest way to do this: https://stackoverflow.com/a/23062482 - cdef set valid_keys_set = set(valid_keys) - cdef str key - - for key in self.value: - if key not in valid_keys_set: - provenance = self.get_node(key).get_provenance() - raise LoadError(LoadErrorReason.INVALID_DATA, - "{}: Unexpected key: {}".format(provenance, key)) - - cpdef object values(self): - return self.value.values() - - ############################################################# - # Private Methods used in BuildStream # - ############################################################# - - cpdef void _assert_fully_composited(self) except *: - cdef str key - cdef Node value - - for key, value in self.value.items(): - # Assert that list composition directives dont remain, this - # indicates that the user intended to override a list which - # never existed in the underlying data - # - if key in ('(>)', '(<)', '(=)'): - provenance = value.get_provenance() - raise LoadError(LoadErrorReason.TRAILING_LIST_DIRECTIVE, - "{}: Attempt to override non-existing list".format(provenance)) - - value._assert_fully_composited() - - # _composite() - # - # Compose one mapping node onto another - # - # Args: - # target (Node): The target to compose into - # - # Raises: LoadError - # - cpdef void _composite(self, MappingNode target) except *: - try: - self.__composite(target, []) - except CompositeError as e: - source_provenance = self.get_provenance() - error_prefix = "" - if source_provenance: - error_prefix = "{}: ".format(source_provenance) - raise LoadError(LoadErrorReason.ILLEGAL_COMPOSITE, - "{}Failure composing {}: {}" - .format(error_prefix, - e.path, - e.message)) from e - - # Like _composite(target, source), but where target overrides source instead. - # - cpdef void _composite_under(self, MappingNode target) except *: - target._composite(self) - - cdef str key - cdef Node value - cdef list to_delete = [key for key in target.value.keys() if key not in self.value] - - for key, value in self.value.items(): - target.value[key] = value - for key in to_delete: - del target.value[key] - - # _find() - # - # Searches the given node tree for the given target node. - # - # This is typically used when trying to walk a path to a given node - # for the purpose of then modifying a similar tree of objects elsewhere - # - # Args: - # target (Node): The node you are looking for in that tree - # - # Returns: - # (list): A path from `node` to `target` or None if `target` is not in the subtree - cpdef list _find(self, Node target): - cdef list path = [] - if self._walk_find(target, path): - return path - return None - - ############################################################# - # Module Local Methods # - ############################################################# - - cdef void _compose_on(self, str key, MappingNode target, list path) except *: - cdef Node target_value - - if self._is_composite_list(): - if key not in target.value: - # Composite list clobbers empty space - target.value[key] = self - else: - target_value = target.value[key] - - if type(target_value) is SequenceNode: - # Composite list composes into a list - self._compose_on_list(target_value) - elif target_value._is_composite_list(): - # Composite list merges into composite list - self._compose_on_composite_dict(target_value) - else: - # Else composing on top of normal dict or a scalar, so raise... - raise CompositeError(path, - "{}: Cannot compose lists onto {}".format( - self.get_provenance(), - target_value.get_provenance())) - else: - # We're composing a dict into target now - if key not in target.value: - # Target lacks a dict at that point, make a fresh one with - # the same provenance as the incoming dict - target.value[key] = MappingNode.__new__(MappingNode, self.file_index, self.line, self.column, {}) - - self.__composite(target.value[key], path) - - cdef void _compose_on_list(self, SequenceNode target): - cdef SequenceNode clobber = self.value.get("(=)") - cdef SequenceNode prefix = self.value.get("(<)") - cdef SequenceNode suffix = self.value.get("(>)") - - if clobber is not None: - target.value.clear() - target.value.extend(clobber.value) - if prefix is not None: - for v in reversed(prefix.value): - target.value.insert(0, v) - if suffix is not None: - target.value.extend(suffix.value) - - cdef void _compose_on_composite_dict(self, MappingNode target): - cdef SequenceNode clobber = self.value.get("(=)") - cdef SequenceNode prefix = self.value.get("(<)") - cdef SequenceNode suffix = self.value.get("(>)") - - if clobber is not None: - # We want to clobber the target list - # which basically means replacing the target list - # with ourselves - target.value["(=)"] = clobber - if prefix is not None: - target.value["(<)"] = prefix - elif "(<)" in target.value: - (<SequenceNode> target.value["(<)"]).value.clear() - if suffix is not None: - target.value["(>)"] = suffix - elif "(>)" in target.value: - (<SequenceNode> target.value["(>)"]).value.clear() - else: - # Not clobbering, so prefix the prefix and suffix the suffix - if prefix is not None: - if "(<)" in target.value: - for v in reversed(prefix.value): - (<SequenceNode> target.value["(<)"]).value.insert(0, v) - else: - target.value["(<)"] = prefix - if suffix is not None: - if "(>)" in target.value: - (<SequenceNode> target.value["(>)"]).value.extend(suffix.value) - else: - target.value["(>)"] = suffix - - cdef Node _get(self, str key, object default, object default_constructor): - value = self.value.get(key, _sentinel) - - if value is _sentinel: - if default is _sentinel: - provenance = self.get_provenance() - raise LoadError(LoadErrorReason.INVALID_DATA, - "{}: Dictionary did not contain expected key '{}'".format(provenance, key)) - - if default is None: - value = None - else: - value = default_constructor.__new__( - default_constructor, _SYNTHETIC_FILE_INDEX, 0, next_synthetic_counter(), default) - - return value - - cdef bint _is_composite_list(self) except *: - cdef bint has_directives = False - cdef bint has_keys = False - cdef str key - - for key in self.value.keys(): - if key in ['(>)', '(<)', '(=)']: - has_directives = True - else: - has_keys = True - - if has_keys and has_directives: - provenance = self.get_provenance() - raise LoadError(LoadErrorReason.INVALID_DATA, - "{}: Dictionary contains array composition directives and arbitrary keys" - .format(provenance)) - - return has_directives - - cdef bint _walk_find(self, Node target, list path) except *: - cdef str k - cdef Node v - - if self._shares_position_with(target): - return True - - for k, v in self.value.items(): - path.append(k) - if v._walk_find(target, path): - return True - del path[-1] - - return False - - ############################################################# - # Private Methods # - ############################################################# - - cdef void __composite(self, MappingNode target, list path=None) except *: - cdef str key - cdef Node value - - for key, value in self.value.items(): - path.append(key) - value._compose_on(key, target, path) - path.pop() - - -cdef class SequenceNode(Node): - - def __cinit__(self, int file_index, int line, int column, list value): - self.value = value - - def __iter__(self): - return iter(self.value) - - def __len__(self): - return len(self.value) - - def __reversed__(self): - return reversed(self.value) - - def __setitem__(self, int key, object value): - cdef Node old_value - - if type(value) in [MappingNode, ScalarNode, SequenceNode]: - self.value[key] = value - else: - node = _create_node_recursive(value, self) - - # FIXME: Do we really want to override provenance? - # See __setitem__ on 'MappingNode' for more context - old_value = self.value[key] - if old_value: - node.file_index = old_value.file_index - node.line = old_value.line - node.column = old_value.column - - self.value[key] = node - - ############################################################# - # Public Methods # - ############################################################# - - cpdef void append(self, object value): - if type(value) in [MappingNode, ScalarNode, SequenceNode]: - self.value.append(value) - else: - node = _create_node_recursive(value, self) - self.value.append(node) - - cpdef list as_str_list(self): - return [node.as_str() for node in self.value] - - cpdef SequenceNode copy(self): - cdef list copy = [] - cdef Node entry - - for entry in self.value: - copy.append(entry.copy()) - - return SequenceNode.__new__(SequenceNode, self.file_index, self.line, self.column, copy) - - cpdef MappingNode mapping_at(self, int index): - value = self.value[index] - - if type(value) is not MappingNode: - provenance = self.get_provenance() - path = ["[{}]".format(p) for p in provenance.toplevel._find(self)] + ["[{}]".format(index)] - raise LoadError(LoadErrorReason.INVALID_DATA, - "{}: Value of '{}' is not of the expected type '{}'" - .format(provenance, path, MappingNode.__name__)) - return value - - cpdef Node node_at(self, int index, list allowed_types = None): - cdef value = self.value[index] - - if allowed_types and type(value) not in allowed_types: - provenance = self.get_provenance() - raise LoadError(LoadErrorReason.INVALID_DATA, - "{}: Value of '{}' is not one of the following: {}.".format( - provenance, index, ", ".join(allowed_types))) - - return value - - cpdef ScalarNode scalar_at(self, int index): - value = self.value[index] - - if type(value) is not ScalarNode: - provenance = self.get_provenance() - path = ["[{}]".format(p) for p in provenance.toplevel._find(self)] + ["[{}]".format(index)] - raise LoadError(LoadErrorReason.INVALID_DATA, - "{}: Value of '{}' is not of the expected type '{}'" - .format(provenance, path, ScalarNode.__name__)) - return value - - cpdef SequenceNode sequence_at(self, int index): - value = self.value[index] - - if type(value) is not SequenceNode: - provenance = self.get_provenance() - path = ["[{}]".format(p) for p in provenance.toplevel._find(self)] + ["[{}]".format(index)] - raise LoadError(LoadErrorReason.INVALID_DATA, - "{}: Value of '{}' is not of the expected type '{}'" - .format(provenance, path, SequenceNode.__name__)) - - return value - - cpdef object strip_node_info(self): - cdef Node value - return [value.strip_node_info() for value in self.value] - - ############################################################# - # Private Methods used in BuildStream # - ############################################################# - - cpdef void _assert_fully_composited(self) except *: - cdef Node value - for value in self.value: - value._assert_fully_composited() - - cdef void _compose_on(self, str key, MappingNode target, list path) except *: - # List clobbers anything list-like - cdef Node target_value = target.value.get(key) - - if not (target_value is None or - type(target_value) is SequenceNode or - target_value._is_composite_list()): - raise CompositeError(path, - "{}: List cannot overwrite {} at: {}" - .format(self.get_provenance(), - key, - target_value.get_provenance())) - # Looks good, clobber it - target.value[key] = self - - cdef bint _is_composite_list(self) except *: - return False - - cdef bint _walk_find(self, Node target, list path) except *: - cdef int i - cdef Node v - - if self._shares_position_with(target): - return True - - for i, v in enumerate(self.value): - path.append(i) - if v._walk_find(target, path): - return True - del path[-1] - - return False - - -# Metadata container for a yaml toplevel node. -# -# This class contains metadata around a yaml node in order to be able -# to trace back the provenance of a node to the file. -# -cdef class FileInfo: - - cdef str filename, shortname, displayname - cdef Node toplevel, - cdef object project - - def __init__(self, str filename, str shortname, str displayname, Node toplevel, object project): - self.filename = filename - self.shortname = shortname - self.displayname = displayname - self.toplevel = toplevel - self.project = project - - -# File name handling -cdef _FILE_LIST = [] - - -# Purely synthetic node will have _SYNTHETIC_FILE_INDEX for the file number, have line number -# zero, and a negative column number which comes from inverting the next value -# out of this counter. Synthetic nodes created with a reference node will -# have a file number from the reference node, some unknown line number, and -# a negative column number from this counter. -cdef int _SYNTHETIC_FILE_INDEX = -1 -cdef int __counter = 0 - -cdef int next_synthetic_counter(): - global __counter - __counter -= 1 - return __counter - - -# Returned from Node.get_provenance -cdef class ProvenanceInformation: - - def __init__(self, Node nodeish): - cdef FileInfo fileinfo - - self._node = nodeish - if (nodeish is None) or (nodeish.file_index == _SYNTHETIC_FILE_INDEX): - self._filename = "" - self._shortname = "" - self._displayname = "" - self._line = 1 - self._col = 0 - self._toplevel = None - self._project = None - else: - fileinfo = <FileInfo> _FILE_LIST[nodeish.file_index] - self._filename = fileinfo.filename - self._shortname = fileinfo.shortname - self._displayname = fileinfo.displayname - # We add 1 here to convert from computerish to humanish - self._line = nodeish.line + 1 - self._col = nodeish.column - self._toplevel = fileinfo.toplevel - self._project = fileinfo.project - self._is_synthetic = (self._filename == '') or (self._col < 0) - - # Convert a Provenance to a string for error reporting - def __str__(self): - if self._is_synthetic: - return "{} [synthetic node]".format(self._displayname) - else: - return "{} [line {:d} column {:d}]".format(self._displayname, self._line, self._col) +from .node cimport FileInfo, MappingNode, Node, ScalarNode, SequenceNode # These exceptions are intended to be caught entirely within # the BuildStream framework, hence they do not reside in the # public exceptions.py -class CompositeError(Exception): - def __init__(self, path, message): - super().__init__(message) - self.path = path - self.message = message - - class YAMLLoadError(Exception): pass @@ -1060,22 +240,6 @@ cdef class Representer: return RepresenterState.init -cdef Node _create_node_recursive(object value, Node ref_node): - cdef value_type = type(value) - - if value_type is list: - node = _new_node_from_list(value, ref_node) - elif value_type is str: - node = ScalarNode.__new__(ScalarNode, ref_node.file_index, ref_node.line, next_synthetic_counter(), value) - elif value_type is dict: - node = _new_node_from_dict(value, ref_node) - else: - raise ValueError( - "Unable to assign a value of type {} to a Node.".format(value_type)) - - return node - - # Loads a dictionary from some YAML # # Args: @@ -1201,48 +365,6 @@ def _new_synthetic_file(str filename, object project=None): return node -# new_node_from_dict() -# -# Args: -# indict (dict): The input dictionary -# -# Returns: -# (Node): A new synthetic YAML tree which represents this dictionary -# -cdef Node _new_node_from_dict(dict indict, Node ref_node): - cdef MappingNode ret = MappingNode.__new__( - MappingNode, ref_node.file_index, ref_node.line, next_synthetic_counter(), {}) - cdef str k - - for k, v in indict.items(): - vtype = type(v) - if vtype is dict: - ret.value[k] = _new_node_from_dict(v, ref_node) - elif vtype is list: - ret.value[k] = _new_node_from_list(v, ref_node) - else: - ret.value[k] = ScalarNode.__new__( - ScalarNode, ref_node.file_index, ref_node.line, next_synthetic_counter(), v) - return ret - - -# Internal function to help new_node_from_dict() to handle lists -cdef Node _new_node_from_list(list inlist, Node ref_node): - cdef SequenceNode ret = SequenceNode.__new__( - SequenceNode, ref_node.file_index, ref_node.line, next_synthetic_counter(), []) - - for v in inlist: - vtype = type(v) - if vtype is dict: - ret.value.append(_new_node_from_dict(v, ref_node)) - elif vtype is list: - ret.value.append(_new_node_from_list(v, ref_node)) - else: - ret.value.append( - ScalarNode.__new__(ScalarNode, ref_node.file_index, ref_node.line, next_synthetic_counter(), v)) - return ret - - # assert_symbol_name() # # A helper function to check if a loaded string is a valid symbol |