Source code for fplore.files.config

# -*- coding: utf-8 -*-
from pyparsing import (Word, Literal, Regex, Keyword, CaselessKeyword, Forward,
                       Group, OneOrMore, ZeroOrMore, alphas, nums, Suppress,
                       delimitedList, CharsNotIn, Empty, Optional, Or,
                       restOfLine)
from fractions import Fraction
from orderedattrdict import AttrDict

from .base import FPLOFileType, loads

AttrDict.__repr__ = AttrDict.__str__ = lambda self: str(dict(self))

IDENTIFIER = Word(alphas + "_", alphas + nums + "_")
INT_DECIMAL = Regex(r'([+-]?(([1-9][0-9]*)|0+))')
INTEGER = INT_DECIMAL.setParseAction(
    lambda s, l, t: int(t[0]))
FLOAT = Regex(r'[+-]?((((\d+\.\d*)|(\d*\.\d+))'
              r'([eE][-+]?\d+)?)|(\d*[eE][+-]?\d+)|INF)').setParseAction(
    lambda s, l, t: float(t[0]))
FLAG = Regex(r'(?P<key>[a-zA-Z_]+)\((?P<val>[+-])\)').setParseAction(
    lambda s, l, t: (t.key, t.val == '+'))
(LPAREN, RPAREN, LBRACK, RBRACK, LBRACE, RBRACE,
 SEMI, COMMA, EQUAL, DQUOTE) = map(Suppress, "()[]{};,=\"")
SIZE = (delimitedList(INTEGER | IDENTIFIER) | INTEGER | Literal('*') |
        IDENTIFIER)
STRING = DQUOTE + ZeroOrMore(CharsNotIn('"')) + DQUOTE
FRACTION = (INTEGER + Literal('/') + INTEGER).setParseAction(
    lambda s, l, t: Fraction(t[0], t[2]))
BOOLEAN = (CaselessKeyword("t") | CaselessKeyword("f")).setParseAction(
    lambda s, l, t: t[0].lower() == "t")

VALUE = Forward()
SVALUE = FLOAT | FRACTION | INTEGER | STRING | BOOLEAN | FLAG
VALUE << (SVALUE | (LBRACE + Group(delimitedList(VALUE) | Empty()
                                   ).setParseAction(
    lambda s, l, t: t.asList()) + RBRACE))
VARIABLE = (IDENTIFIER("name") + Optional(LBRACK + SIZE("size") + RBRACK))
SCALARTYPE = Or(map(Keyword, "int real logical flag char string".split()))
STRUCTMEMBERS = Forward()
STRUCTTYPE = Keyword("struct") + LBRACE + STRUCTMEMBERS("members") + RBRACE
DECLARATION = ((SCALARTYPE | STRUCTTYPE)("type") +
               Optional(LBRACK + SIZE + RBRACK) + VARIABLE)
STRUCTMEMBERS << Group(ZeroOrMore(Group(DECLARATION + SEMI)))
DECL_ASS_STMT = DECLARATION + Optional(EQUAL + VALUE("value")) + SEMI

SECTION = (Keyword('section') + IDENTIFIER('name') + LBRACE +
           Group(OneOrMore(Group(DECL_ASS_STMT)))('declarations') +
           RBRACE + SEMI)
COMMENT = '#' + restOfLine  # todo: could potentially match '#' within strings?
CONFIG = Group(OneOrMore(Group(SECTION)))
CONFIG.ignore(COMMENT)


[docs]def walk(ns, declaration, value): # walks through declaration tokens recursively and constructs namespace if declaration.type[0] == 'struct': if declaration.size: # for struct arrays subdecs_vals = [] for i, v in enumerate(value): fake_declaration = declaration.copy() fake_declaration['name'] = i # for list indexing fake_declaration['size'] = 0 # prevents infinite recursion subdecs_vals.append((fake_declaration, v)) ns[declaration.name] = [None] * len(value) else: # scalar structs ns[declaration.name] = AttrDict() subdecs_vals = zip(declaration.members, value) for subdec, val in subdecs_vals: for d in walk(ns[declaration.name], subdec, val): yield d else: yield ns, declaration.name, value
[docs]class FPLOConfig(object, metaclass=FPLOFileType): load_default = True
[docs] @loads('_data') def load(self): with open(self.filepath, 'r') as config_file: config_str = config_file.read() return self.parse_config(config_str)
@property def sections(self): return dict(self._data).keys() def __getattr__(self, item): try: return self.__getattribute__(item) except AttributeError: if item == "_load_cache": raise return self._data[item]
[docs] @staticmethod def parse_config(config_str): tokens = CONFIG.parseString(config_str, parseAll=True) namespace = AttrDict() for config in tokens: for section in config: section_ns = namespace[section.name] = AttrDict() for declaration in section.declarations: for nsnode, name, value in walk( section_ns, declaration, declaration.value): nsnode[name] = value return namespace