Nie byłem pewien, jak to szukać, ale staram się skrypt, który dynamicznie uruchamia programy. Będę miał kilka plików JSON i chcę być w stanie wykonać wyszukiwanie zastąpić coś.

Więc skonfiguruję przykład:

Config.json.

{
    "global_vars": {
        "BASEDIR": "/app",
        "CONFIG_DIR": "{BASEDIR}/config",
        "LOG_DIR": "{BASEDIR}/log",
        "CONFIG_ARCHIVE_DIR": "{CONFIG_DIR}/archive"
    }
}

Następnie proces.json.

{
    "name": "Dummy_Process",
    "binary": "java",
    "executable": "DummyProcess-0.1.0.jar",
    "launch_args": "-Dspring.config.location={CONFIG_DIR}/application.yml -Dlogging.config={CONFIG_DIR}/logback-spring.xml -jar {executable}",
    "startup_log": "{LOG_DIR}/startup_{name}.out"
}

Teraz chcę być w stanie załadować oba te obiekty JSON i być w stanie korzystać z wartości do aktualizacji. Tak jak "config_archive_dir": "{config_dir} / archiwum" stanie się config_archive_dir ":" / app / config / archiwum "

Czy ktoś zna dobry sposób na wykonanie tego rekurencyjnie, ponieważ prowadzę w kwestiach, kiedy próbuję użyć czegoś takiego jak Config_dir, który najpierw wymaga.

Mam tę funkcję, która ładuje wszystkie dane:

#Recursive function, loops and loads all values into data
    def _load_data(data,obj):
        for i in obj.keys():
            if isinstance(obj[i],str):
                data[i]=obj[i]
            if isinstance(obj[i],dict):
                data=_load_data(data,obj[i])
        return data

Wtedy mam tę funkcję:

def _update_data(data,data_str=""):
    if not data_str:
        data_str=json.dumps(data)

    for i in data.keys():
        if isinstance(data[i],str):
            data_str=data_str.replace("{"+i+"}",data[i])
        if isinstance(data[i],dict):
            data=_update_data(data,data_str)
    return json.loads(data_str)

Działa więc na jeden poziom, ale nie wiem, czy to najlepszy sposób na to zrobić. Zatrzymuje działanie, gdy uderzyłem w przypadek jak config_dir, ponieważ wiele razy musiałby pętlować się po danych. Najpierw musi zaktualizować Letnier, a następnie jeszcze raz, aby zaktualizować Config_dir. sugestia Witamy.

Końcowy cel tego skryptu jest utworzenie skryptu Start / Stop / Status w celu zarządzania wszystkimi naszych binariach. Wszyscy używają różnych binarnych do rozpoczęcia i chcę jednego pliku procesów dla wielu serwerów. Każdy proces będzie miał tablicę serwerów, aby powiedzieć skrypt start / stop, co uruchomić na danym serwerze. Może jest coś takiego, więc jeśli jest, proszę, w kierunku wskazać w kierunku.

Będę biegać na Linuksie i wolę używać Pythona. Chcę coś inteligentnego i łatwy dla kogoś innego do odbicia i użycia / modyfikacji.

1
Ponzi314 26 czerwiec 2017, 23:45

3 odpowiedzi

Najlepsza odpowiedź

Zrobiłem coś, co działa z podanymi plikami przykładowymi. Zauważ, że nie poradziłem sobie z wielu kluczy ani słowników w danych. Ta funkcja akceptuje listę słowników uzyskanych po parsowaniu plików wejściowych. Wykorzystuje fakt, że re.sub może zaakceptować funkcję wartości zamiennej i połączenia, które funkcjonują przy każdym meczu. Jestem pewien, że istnieje wiele ulepszeń, które mogłyby być wykonane, ale powinno się przynajmniej zacząć.

def make_config(configs):
    replacements = {}

    def find_defs(config):
        # Find leaf nodes of the dictionary.
        defs = {}
        for k, v in config.items():
            if isinstance(v, dict):
                # Nested dictionary so recurse.
                defs.update(find_defs(v))
            else:
                defs[k] = v
        return defs

    for config in configs:
        replacements.update(find_defs(config))

    def make_replacement(m):
        # Construct the replacement string.
        name = m.group(0).strip('{}')
        if name in replacements:
            # Replace replacement strings in the replacement string.
            new = re.sub('\{[^}]+\}', make_replacement, replacements[name])
            # Cache result
            replacements[name] = new
            return new
        raise Exception('Replacement string for {} not found'.format(name))

    finalconfig = {}
    for name, value in replacements.items():
        finalconfig[name] = re.sub('\{[^}]+\}', make_replacement, value)

    return finalconfig

Z tym wejściem:

[
    {
        "global_vars": {
            "BASEDIR": "/app",
            "CONFIG_DIR": "{BASEDIR}/config",
            "LOG_DIR": "{BASEDIR}/log",
            "CONFIG_ARCHIVE_DIR": "{CONFIG_DIR}/archive"
        }
    },
    {
        "name": "Dummy_Process",
        "binary": "java",
        "executable": "DummyProcess-0.1.0.jar",
        "launch_args": "-Dspring.config.location={CONFIG_DIR}/application.yml -Dlogging.config={CONFIG_DIR}/logback-spring.xml -jar {executable}",
        "startup_log": "{LOG_DIR}/startup_{name}.out"
    }
]

Daje to produkcję:

{
    'BASEDIR': '/app',
    'CONFIG_ARCHIVE_DIR': '/app/config/archive',
    'CONFIG_DIR': '/app/config',
    'LOG_DIR': '/app/log',
    'binary': 'java',
    'executable': 'DummyProcess-0.1.0.jar',
    'launch_args': '-Dspring.config.location=/app/config/application.yml -Dlogging.config=/app/config/logback-spring.xml -jar DummyProcess-0.1.0.jar',
    'name': 'Dummy_Process',
    'startup_log': '/app/log/startup_Dummy_Process.out'
}
1
FamousJameous 26 czerwiec 2017, 22:12

Wdrożyłem rozwiązanie z klasą (config) z kilkoma funkcjami:

  1. _load: Po prostu przekonwertuj z JSON do obiektu Pythona;
  2. _extract_params: pętla nad dokumentem (wyjście _load) i dodaj je do obiektu klasy (self.Params);
  3. _Loop: pętla nad obiektem zwrócona z _extract_params i, jeśli wartości zawiera dowolne {param}, wywołaj metodę _transform;
  4. _TRANSFORM: Wymień wartość {param} w wartościach poprawnymi wartościami, jeśli istnieje jakaś "{" w wartości powiązanej z paramorem, który należy wymienić, zadzwoń ponownie funkcję

Mam nadzieję, że nie byłbym wystarczająco jasny, oto kod:

import json
import re

config = """{
    "global_vars": {
        "BASEDIR": "/app",
        "CONFIG_DIR": "{BASEDIR}/config",
        "LOG_DIR": "{BASEDIR}/log",
        "CONFIG_ARCHIVE_DIR": "{CONFIG_DIR}/archive"
    }
}"""

process = """{
    "name": "Dummy_Process",
    "binary": "java",
    "executable": "DummyProcess-0.1.0.jar",
    "launch_args": "-Dspring.config.location={CONFIG_DIR}/application.yml -Dlogging.config={CONFIG_DIR}/logback-spring.xml -jar {executable}",
    "startup_log": "{LOG_DIR}/startup_{name}.out"
}
"""
class Config(object):
def __init__(self, documents):
    self.documents = documents
    self.params = {}
    self.output = {}

# Loads JSON to dictionary
def _load(self, document):
    obj = json.loads(document)
    return obj

# Extracts the config parameters in a dictionary
def _extract_params(self, document):
    for k, v in document.items():
        if isinstance(v, dict):
            # Recursion for inner dictionaries
            self._extract_params(v)
        else:
            # if not a dict set params[k] as v
            self.params[k] = v
    return self.params

# Loop on the configs dictionary
def _loop(self, params):
    for key, value in params.items():
        # if there is any parameter inside the value
        if len(re.findall(r'{([^}]*)\}', value)) > 0:
            findings = re.findall(r'{([^}]*)\}', value)
            # call the transform function
            self._transform(params, key, findings)
    return self.output

# Replace all the findings with the correct value
def _transform(self, object, key, findings):
    # Iterate over the found params
    for finding in findings:
        # if { -> recursion to set all the needed values right
        if '{' in object[finding]:
            self._transform(object, finding, re.findall(r'{([^}]*)\}', object[finding]))
        # Do de actual replace
        object[key] = object[key].replace('{'+finding+'}', object[finding])

    self.output = object
    return self.output

# Entry point
def process_document(self):
    params = {}
    # _load the documents and extract the params
    for document in self.documents:
        params.update(self._extract_params(self._load(document)))
    # _loop over the params
    return self._loop(params)

    # return self.output

if __name__ == '__main__':
    config = Config([config, process])
    print(config.process_document())

Jestem pewien, że istnieje wiele innych lepszych sposobów na osiągnięcie celu, ale nadal mam nadzieję, że może to przydatne dla ciebie.

0
user9883448user9883448 28 czerwiec 2017, 09:51

Jako alternatywa dla odpowiedzi @famousJeous i jeśli nie masz nic przeciwko zmianie na format ini, można również użyć wbudowanego Pythona configparser, który ma już wsparcie, aby rozwinąć zmienne.

0
Melvyn 26 czerwiec 2017, 22:30