Как заставить пользовательский тег YAML работать с псевдонимом последовательности в pyyaml - PullRequest
0 голосов
/ 14 декабря 2018

У меня есть файл yaml с такими псевдонимами:

vars:
  users: &users ['user1', 'user2', 'user3', 'user4']

refs:
  users: *users
  user: !rand ['user1', 'user2', 'user3', 'user4']

!rand - это пользовательский тег, который вызывает Python random.choice для последовательности yaml.

I 'используя следующую реализацию тега !rand:

import random
import yaml

def load_yaml(file):
    def rand_constructor(loader, node):
        value = loader.construct_sequence(node)
        return random.choice(value)

    yaml.add_constructor('!rand', rand_constructor)

    with open(file) as f:
        return yaml.load(f)

Он работает, как ожидалось, и user получает случайное значение из users.Теперь, когда я использую !rand с псевдонимом *users, он перестает работать:

vars:
  users: &users ['user1', 'user2', 'user3', 'user4']

refs:
  users: *users
  user: !rand *users

Ошибка:

File ../python3.6/site-packages/yaml/parser.py", line 439, in parse_block_mapping_key
    "expected <block end>, but found %r" % token.id, token.start_mark)
yaml.parser.ParserError: while parsing a block mapping
  in "/temp/config.yml", line 5, column 3
expected <block end>, but found '<alias>'
  in "/temp/config.yml", line 6, column 18

Как мне заставить работать псевдонимы последовательностей?

Ответы [ 2 ]

0 голосов
/ 08 января 2019

Вы также можете сделать свой список вызываемым и вызывать его каждый раз, когда вам нужен случайный пользователь:

YAML = """
user: &users !rand 
    - user1
    - user2
    - user3
    - user4
"""

import random
import yaml
from collections.abc import Sequence

class RandomizableList(Sequence):
    def __init__(self, items):
        self.items = items
    def __len__(self):
        return len(self.items)
    def __getitem__(self, value):
        return self.items[value]
    def __call__(self):
        return random.choice(self.items)
    def __repr__(self):
        return repr(self.items)

def rand_constructor(loader, node):
    return RandomizableList(loader.construct_sequence(node))

yaml.add_constructor('!rand', rand_constructor)
result = yaml.load(YAML)
for i in range(4):
    print(result['user']())
print(result['user'])
0 голосов
/ 14 декабря 2018

В YAML теги предназначены для определения типа контента.Они не предназначены для обработки содержимого.

По этой причине вы не можете пометить псевдоним, поскольку псевдоним - это просто ссылка на контент, который уже был помечен в том месте, где он определен.,В вашем случае ваша последовательность получит тег !!seq в соответствии с базовой схемой YAML .YAML не предоставляет возможности повторно пометить его.

Сказав это, вы, конечно, можете обойти это: Оберните параметр !rand в последовательность:

vars:
  users: &users ['user1', 'user2', 'user3', 'user4']

refs:
  users: *users
  user: !rand [*users]

Youтогда просто нужно изменить свой код на:

import random
import yaml

def load_yaml(file):
    def rand_constructor(loader, node):
        value = loader.construct_sequence(node)
        return random.choice(value[0])

    yaml.add_constructor('!rand', rand_constructor)

    with open(file) as f:
        return yaml.load(f)

Но имейте в виду, что прямой вызов !rand тогда выглядит так:

vars:
  users: &users ['user1', 'user2', 'user3', 'user4']

refs:
  users: *users
  user: !rand [['user1', 'user2', 'user3', 'user4']]

Видите это как внешнюю последовательностьсписок параметров функции !rand (которая принимает один параметр) и внутренняя последовательность, являющаяся этим параметром.

...