Неожиданное поведение при использовании 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)
# ^^^^ ^