Использовать Python f-строки с файлами Yaml?

Если у меня есть файл yaml, содержащий строку с нотацией в скобках {}, используемую совместно с f-строками python, как можно использовать здесь интерполяцию f-строки? Возьмем, к примеру, этот простой файл yaml:

# tmp.yaml
k1: val1
k2: val2 as well as {x}

Если x = 'val3'Я хотел бы, чтобы значение k2 отражало val2 as well as val3

# app.py
x = 'val3'
with open('tmp.yaml', 'rt') as f:
    conf = yaml.safe_load(f)

print(conf)
{'k1': 'val1', 'k2': 'val2 as well as {x}'}

Это может быть сделано довольно легко с форматными строками...

print(conf['k2'].format(x=x))
val2 as well as val3

Но как сделать то же самое с f-струнами?

3 ответа

Я нашел это jinja2 предоставляет самое простое решение этой проблемы.

Исходный файл yaml:

# tmp.yaml
k1: val1
k2: val2 as well as {{ x }}

Прочтите файл и выполните рендеринг с использованием шаблона jinja:

with open('tmp.yaml', 'rt') as f:
    conf = f.read().rstrip()

print(conf)
# 'k1: val1\nk2: val2 as well as {{ x }}'

import jinja2
template = Template(conf)
conf = template.render(x='val3')
config = yaml.safe_load(conf)

print(config)
# {'k1': 'val1', 'k2': 'val2 as well as val3'}

Вы можете определить собственный конструктор:

import yaml

values = { 'x': 'val3' }

def format_constructor(loader, node):
  return loader.construct_scalar(node).format(**values)

yaml.SafeLoader.add_constructor(u'!format', format_constructor)

conf = yaml.safe_load("""
k1: val1
k2: !format val2 as well as {x}
""")

print(conf)

Если вы не хотите использовать тег !formatВы также можете использовать add_constructor с u'tag:yaml.org,2002:str' как тег Это заменит строковый конструктор по умолчанию на ваш.

Учитывая вышеупомянутые ответы, все из которых хороши, существует пакет Python для интеллектуального создания объектов из YAML / JSON / dicts, который активно разрабатывается и расширяется. (полное раскрытие, я являюсь соавтором этого пакета , см. здесь )

Кроме того, есть варианты передачи аргументов, см. Это

Затем вы можете определить это в своем YAML:

      some_field: _|arg_name|_

И загрузите это так:

      test_conf_yaml = PickleRick('./tests/placebos/test_config.yaml', arg_name='hallo world')

Установить:

pip install pickle-rick

Использовать:

Определите строку (или файл) YAML или JSON.

      BASIC:
 text: test
 dictionary:
   one: 1
   two: 2
 number: 2
 list:
   - one
   - two
   - four
   - name: John
     age: 20
 USERNAME:
   type: env
   load: USERNAME
 callable_lambda:
   type: lambda
   load: "lambda: print('hell world!')"
 datenow:
   type: lambda
   import:
     - "from datetime import datetime as dd"
   load: "lambda: print(dd.utcnow().strftime('%Y-%m-%d'))"
 test_function:
   type: function
   name: test_function
   args:
     x: 7
     y: null
     s: hello world
     any:
       - 1
       - hello
   import:
     - "math"
   load: >
     def test(x, y, s, any):
       print(math.e)
       iii = 111
       print(iii)
       print(x,s)
       if y:
         print(type(y))
       else:
         print(y)
       for i in any:
         print(i)

Затем используйте его как объект.

      >> from pickle_rick import PickleRick

>> config = PickleRick('./config.yaml', deep=True, load_lambda=True)

>> config.BASIC.dictionary
{'one' : 1, 'two' : 2}

>> config.BASIC.callable_lambda()
hell world!

Вы можете определять функции Python, загружать дополнительные данные из других файлов или REST API, переменных среды, а затем снова записывать все в YAML или JSON.

Это особенно хорошо работает при создании систем, требующих структурированных файлов конфигурации, или в записных книжках в виде интерактивных структур.

Это примечание по безопасности. Загружайте только те файлы, которым вы доверяете, так как любой код может быть выполнен, поэтому избегайте простой загрузки чего-либо, не зная, каково полное содержимое.

Пакет называется PickleRick и доступен здесь:

Другие вопросы по тегам