Python YAML parameter (get value from another parameter)

I am trying to use a YAML config file in my Python script, one of the problems I have found is that I cannot access other attributes, so I need to duplicate a lot of content.

for example

root_path: /root
script_path: root_path + /scripts

      

This of course doesn't exist, but is there a way to achieve this? Because there is a lot of content that I cannot duplicate, because when someone changes, I need to change it everywhere ...

I also looked at creating my own connection function

root_path: &ROOT /root
script_path: !join [*ROOT, '/scripts']


def join(loader, node):
    seq = loader.construct_sequence(node)
    return ''.join([str(i) for i in seq])

yaml.add_constructor('!join', join)

      

But I have to set every time &VARNAME

, it would be nice to automatically set the parameter key as a reference object ...

+3


source to share


3 answers


Ok, I was working on a solution because nothing was what I wanted and everything was so hard, I wanted to die ...

These solutions convert string %something%

to value for something

.

It works great, this is an example

root_path: /root
script_path: "%root%/scripts"

      

With this method, it script_path

becomes /root/scripts

.



def replace_values(yaml_file):
    def _get(dict, list):
        return reduce(lambda d, k: d[k], list, dict)

    def _replace(obj):
        for k, v in obj.iteritems():
            if isinstance(v, dict):
                _replace(v)
            if isinstance(v, str):
                match = re.match(r'%(.*)%', v)
                if match:
                    reference = match.group(1).split('.')
                    replace = _get(yaml_file, reference)
                    obj[k] = re.sub(r'%.*%', replace, v)

    _replace(yaml_file)
    return yaml_file

      

Usage is easy, just load the file in the Yaml

usual way and replace the call.

with open(config_file.get(options.env), 'r') as ymlfile:
    config = yaml.load(ymlfile)

config = replace_values(config)

      

Then our new one will config

keep the replaced values, it won't overwrite the original file of .yml

course. I hope you find it useful, I am actually doing this because this is exactly what I need.

I used the percentage token %

, but you can change it to whatever you want and change the method to make it work with regex (some tokens are used by regex, so I used %

)

+1


source


You cannot do this with YAML. For a config file, you can instead use ConfigParser , which allows you to interpolate values โ€‹โ€‹so that your example looks like this:



root_path: /root
script_path: %(root_path)s/scripts 

      

0


source


I would reinforce the YAML parser a bit ยน:

import ruamel.yaml as yaml
from ruamel.yaml.comments import CommentedMap

yaml_str = """\
root_path: /root   # use this key for expansion of values
script_path: root_path + /scripts
"""

def set_item(self, key, value):
    split_val = value.split(' + ', 1)
    if len(split_val) > 1:
        alt_key = split_val[0].strip()
        if alt_key in self.keys():
            value = self.get(alt_key) + split_val[1]
    self._org__setitem__(key, value)

CommentedMap._org__setitem__ = CommentedMap.__setitem__
CommentedMap.__setitem__ = set_item

data = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)
print yaml.dump(data, Dumper=yaml.RoundTripDumper)

      

will provide you with:

root_path: /root   # use this key for expansion of values
script_path: /root/scripts

      

Note that both comment and key order are preserved using the RoundTripLoader / Dumper combination. data

works like regular Python dict

.


ยน This was done using ruamel.yaml , of which I am the author. It is a superset of PyYAML primarily for storing comments and other crawling information.

0


source







All Articles