Разбор заводного файла в python

У меня есть отличный конфигурационный файл, к которому я тоже хочу добавить данные. Было бы проще собирать данные с использованием python, который я хочу добавить, но я не смог найти соответствующий модуль ConfigSlurper в python, и я не нашел простого способа сделать это с помощью ConfigParser или чего-либо еще. Кто-нибудь сделал что-то подобное, что имеет некоторые отзывы / советы по лучшему подходу?

2 ответа

Это было забавное упражнение.

from shlex import shlex
from ast import literal_eval

TRANSLATION = {
        "true": True,
        "false": False,
        "null": None,
    }

class ParseException(Exception):
    def __init__(self, token, line):
        self.token = token
        self.line = line
    def __str__(self):
        return "ParseException at line %d: invalid token %s" % (self.line, self.token)

class GroovyConfigSlurper:
    def __init__(self, source):
        self.source = source

    def parse(self):
        lex = shlex(self.source)
        lex.wordchars += "."
        state = 1
        context = []
        result = dict()
        while 1:
            token = lex.get_token()
            if not token:
                return result
            if state == 1:
                if token == "}":
                    if len(context):
                        context.pop()
                    else:
                        raise ParseException(token, lex.lineno)
                else:
                    name = token
                    state = 2
            elif state == 2:
                if token == "=":
                    state = 3
                elif token == "{":
                    context.append(name)
                    state = 1
                else:
                    raise ParseException(token, lex.lineno)
            elif state == 3:
                try:
                    value = TRANSLATION[token]
                except KeyError:
                    value = literal_eval(token)
                key = ".".join(context + [name]).split(".")
                current = result
                for i in xrange(0, len(key) - 1):
                    if key[i] not in current:
                        current[key[i]] = dict()
                    current = current[key[i]]
                current[key[-1]] = value
                state = 1

Затем вы можете сделать

with open("test.conf", "r") as f:
    print GroovyConfigSlurper(f).parse()
# => {'setting': {'smtp': {'mail': {'host': 'smtp.myisp.com', 'auth': {'user': 'server'}}}}, 'grails': {'webflow': {'stateless': True}}, 'resources': {'URL': 'http://localhost:80/resources'}}

Ответ user240443Я мог бы также предложить две небольшие модификации, они были необходимы в нашем случае (анализ файлов конфигурации Nextflow на основе Groovy из базы кода Python):

  1. поддержка отрицательных чисел (т. е. монадического знака минус)
  2. поддержка однострочных комментариев в стиле Groovy (т.е. //)

Также добавлен простой служебный метод для записи JSON в файл и добавлена ​​возможность передавать строковое имя файла вместо строкового объекта.

Обновленный код выглядит следующим образом:

      from shlex import shlex
from ast import literal_eval

TRANSLATION = {
    "true": True,
    "false": False,
    "null": None,
}


class ParseException(Exception):
    def __init__(self, token, line):
        self.token = token
        self.line = line

    def __str__(self):
        return "ParseException at line %d: invalid token %s" % (self.line, self.token)


class GroovyConfigParser:
    def __init__(self, source):
        if isinstance(source, str):
            self.source = open(source)
            self.should_close_source = True
        else:
            self.source = source
            self.should_close_source = False

    def __del__(self):
        if self.should_close_source and not self.source.closed:
            self.source.close()

    def parse(self):
        lex = shlex(self.source)
        lex.wordchars = lex.wordchars + ".-"
        lex.commenters = "//"
        state = 1
        context = []
        result = dict()
        while True:
            token = lex.get_token()
            if not token:
                return result
            if state == 1:
                if token == "}":
                    if len(context):
                        context.pop()
                    else:
                        raise ParseException(token, lex.lineno)
                else:
                    name = token
                    state = 2
            elif state == 2:
                if token == "=":
                    state = 3
                elif token == "{":
                    context.append(name)
                    state = 1
                else:
                    raise ParseException(token, lex.lineno)
            elif state == 3:
                try:
                    value = TRANSLATION[token]
                except KeyError:
                    value = literal_eval(token)
                key = ".".join(context + [name]).split(".")
                current = result
                for i in range(len(key) - 1):
                    if key[i] not in current:
                        current[key[i]] = dict()
                    current = current[key[i]]
                current[key[-1]] = value
                state = 1

    def write_as_json_file(self, json_file):
        import json
        with open(json_file, 'w') as file:
            json.dump(self.parse(), file, indent=4)
Другие вопросы по тегам