Неожиданное поведение при использовании YAML Storage с TinyDB

Я использую TinyDB для небольшой утилиты CLI для управления черновиками личных документов. База данных хранит метаданные для каждого черновика; файл должен быть доступен для редактирования человеком (чтобы я мог добавить детали вручную), и по этой причине я хотел бы использовать YAML поверх JSON в качестве формата.

Я реализовал YamlStorage подклассы классов storages.Storage как указано в документах TinyDB:

class TestYamlStorage(Storage):
    """
    Store the data in a YAML file.
    Written following the example at http://tinydb.readthedocs.io/en/latest/extend.html#write-a-custom-storage
    """
    def __init__(self, filename):  # (1)
        super().__init__()
        self.filename = filename
        touch(filename)

    def read(self):
        with open(self.filename) as handle:
            try:
                data = yaml.load(handle.read())
                return data
            except yaml.YAMLError:
                return None  # (3)

    def write(self, data):
        print('writing data: {}'.format(data))
        with open(self.filename, 'w') as handle:
                yaml.dump(data, handle)


    def close(self):  # (4)
        pass

Все работает нормально, когда вставляется только один элемент или несколько элементов одновременно insert_multiple:

db = TinyDB('db.yaml', storage=TestYamlStorage)
dicts = [
    dict(name='Homer', age=38),
    dict(name='Marge', age=34),
    dict(name='Bart', age=10)
]

# this works as expected
db.insert_multiple(dicts)

Результирующий db.yaml:

_default:
  1: {age: 38, name: Homer}
  2: {age: 34, name: Marge}
  3: {age: 10, name: Bart}

Однако при вставке элементов несколько раз с insert, полученный файл YAML отличается:

db = TinyDB('db.yaml', storage=TestYamlStorage)

db.insert(dict(name='Homer', age=38))
db.insert(dict(name='Bart', age=10))

db.yaml:

_default:
  1: !!python/object/new:tinydb.database.Element
    dictitems: {age: 38, name: Homer}
    state: {eid: 1}
  2: {age: 10, name: Bart}

Данные в этом формате (кроме того, что они выглядят более грязными), похоже, не совместимы с yaml.safe_load (вызов db.all() возвращается []). Моя интерпретация заключается в том, что процесс сериализации YAML в некотором роде "перегружен", то есть что Element экземпляр пишется db.yaml вместо базовых данных.

Что-то не так с моим кодом? Я попытался поработать с параметрами PyYAML, используя другой модуль YAML (ruamel.yaml), и создать второй класс YamlStorage, копируемый из JSONStorage по умолчанию, но без каких-либо различий.

Информация о версии: Python 3.4.3, TinyDB 3.2.0, PyYAML 3.11. Я разместил готовый MWE со всем импортом здесь.

редактировать

После предложения @Anthon я попытался распечатать вывод YAML в sys.stdout непосредственно перед сбросом в файл. Проблема воспроизводится и в этом случае. Смотри блокнот.

1 ответ

Когда вы обновляете существующую "базу данных", вы получаете database.Element которая включает (как вы можете видеть во втором файле YAML) информацию о состоянии.

Когда это снова сохраняется, вы не сохраняете dict, но пример этого Element который является подклассом dict и для этого ruamel.yaml (и PyYAML) должен хранить оба dictitems (пары ключ-значение для dict) и state (словарь, представляющий эти атрибуты и их значения).

Преобразование вашего элемента в dict Явно перед написанием надо сделать свое дело:

    def write(self, data):
        print('writing data: {}'.format(data))
        with open(self.filename, 'w') as handle:
                yaml.dump(dict(data), handle)
    #                      ^^^^    ^
Другие вопросы по тегам