Module sverchok.utils.yaml_parser
Limited implementation of reading yaml files. Should be replaced when such library is available in build-in Python source.
Expand source code
"""
Limited implementation of reading yaml files. Should be replaced when such
library is available in build-in Python source.
"""
def load(file):
with open(file) as yaml_lines:
stack: list = []
for raw_line in yaml_lines:
line = YamlLine(raw_line)
if line.is_blank or line.is_comment:
continue
while len(stack) > line.indent_level:
stack.pop()
current = stack[-1] if stack else ...
if line.is_list_value:
# init root list
if not stack:
data = []
stack.append(data)
current = data
# replace None dict value with a list
if current is None: # all dicts can keep only dicts for now
new_list = []
for key in stack[-2].keys(): # should have only one key
stack[-2][key] = new_list
break
stack[-1] = new_list
current = new_list
if line.is_dict_value: # it is list item and new dictionary
if not isinstance(current, list):
raise TypeError("A list was expected here")
new_dict = {line.key: line.dict_value}
current.append(new_dict)
stack.append(new_dict)
stack.append(line.dict_value)
else: # new list item to a list
stack[-1].append(line.list_value)
elif line.is_dict_value:
raise TypeError(f'Dictionary values are excepted only as part of some list - "{raw_line}"')
else:
raise TypeError(f'Any value should be either list of dictionary key - "{raw_line}"')
return data
class YamlLine:
def __init__(self, line):
self._line: str = self._remove_comment(line)
@property
def is_comment(self):
return self._line.strip().startswith('#')
@property
def is_blank(self):
return not self._line.strip()
@property
def is_list_value(self):
return self._line.strip().startswith('-')
@property
def is_dict_value(self):
line = self._line.expandtabs(1).strip()
return ': ' in line or ':' == line[-1]
@property
def indent_level(self):
# https://docs.python.org/3/reference/lexical_analysis.html#indentation
first_letter, *_ = self._line.strip()
indention = self._line.expandtabs(4).index(first_letter)
return (indention // 4) * 2 + 1 # fragile
@property
def key(self):
line = self._line.expandtabs(1).strip()
if line.startswith('-'):
line = line[1:].strip()
if ':' == line[-1]:
return line[:-1].strip()
key, value = line.split(': ')
return key.strip()
@property
def list_value(self):
line = self._line.expandtabs(1).strip()
if self.is_list_value:
return line.split('-', 1)[1].strip()
@property
def dict_value(self):
line = self._line.expandtabs(1).strip()
if ':' == line[-1]:
return None
key, value = line.split(': ')
return value.strip().strip("'\"") # always return as a string for now
@staticmethod
def _remove_comment(line):
line, _, comment = line.partition(' #') # can be incorrect inside string values
return line
if __name__ == '__main__':
from pprint import pprint
pprint(load('../index.yaml'))
Functions
def load(file)
-
Expand source code
def load(file): with open(file) as yaml_lines: stack: list = [] for raw_line in yaml_lines: line = YamlLine(raw_line) if line.is_blank or line.is_comment: continue while len(stack) > line.indent_level: stack.pop() current = stack[-1] if stack else ... if line.is_list_value: # init root list if not stack: data = [] stack.append(data) current = data # replace None dict value with a list if current is None: # all dicts can keep only dicts for now new_list = [] for key in stack[-2].keys(): # should have only one key stack[-2][key] = new_list break stack[-1] = new_list current = new_list if line.is_dict_value: # it is list item and new dictionary if not isinstance(current, list): raise TypeError("A list was expected here") new_dict = {line.key: line.dict_value} current.append(new_dict) stack.append(new_dict) stack.append(line.dict_value) else: # new list item to a list stack[-1].append(line.list_value) elif line.is_dict_value: raise TypeError(f'Dictionary values are excepted only as part of some list - "{raw_line}"') else: raise TypeError(f'Any value should be either list of dictionary key - "{raw_line}"') return data
Classes
class YamlLine (line)
-
Expand source code
class YamlLine: def __init__(self, line): self._line: str = self._remove_comment(line) @property def is_comment(self): return self._line.strip().startswith('#') @property def is_blank(self): return not self._line.strip() @property def is_list_value(self): return self._line.strip().startswith('-') @property def is_dict_value(self): line = self._line.expandtabs(1).strip() return ': ' in line or ':' == line[-1] @property def indent_level(self): # https://docs.python.org/3/reference/lexical_analysis.html#indentation first_letter, *_ = self._line.strip() indention = self._line.expandtabs(4).index(first_letter) return (indention // 4) * 2 + 1 # fragile @property def key(self): line = self._line.expandtabs(1).strip() if line.startswith('-'): line = line[1:].strip() if ':' == line[-1]: return line[:-1].strip() key, value = line.split(': ') return key.strip() @property def list_value(self): line = self._line.expandtabs(1).strip() if self.is_list_value: return line.split('-', 1)[1].strip() @property def dict_value(self): line = self._line.expandtabs(1).strip() if ':' == line[-1]: return None key, value = line.split(': ') return value.strip().strip("'\"") # always return as a string for now @staticmethod def _remove_comment(line): line, _, comment = line.partition(' #') # can be incorrect inside string values return line
Instance variables
var dict_value
-
Expand source code
@property def dict_value(self): line = self._line.expandtabs(1).strip() if ':' == line[-1]: return None key, value = line.split(': ') return value.strip().strip("'\"") # always return as a string for now
var indent_level
-
Expand source code
@property def indent_level(self): # https://docs.python.org/3/reference/lexical_analysis.html#indentation first_letter, *_ = self._line.strip() indention = self._line.expandtabs(4).index(first_letter) return (indention // 4) * 2 + 1 # fragile
var is_blank
-
Expand source code
@property def is_blank(self): return not self._line.strip()
var is_comment
-
Expand source code
@property def is_comment(self): return self._line.strip().startswith('#')
var is_dict_value
-
Expand source code
@property def is_dict_value(self): line = self._line.expandtabs(1).strip() return ': ' in line or ':' == line[-1]
var is_list_value
-
Expand source code
@property def is_list_value(self): return self._line.strip().startswith('-')
var key
-
Expand source code
@property def key(self): line = self._line.expandtabs(1).strip() if line.startswith('-'): line = line[1:].strip() if ':' == line[-1]: return line[:-1].strip() key, value = line.split(': ') return key.strip()
var list_value
-
Expand source code
@property def list_value(self): line = self._line.expandtabs(1).strip() if self.is_list_value: return line.split('-', 1)[1].strip()