2016-05-17 8 views
1

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

Я реализовал 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. Я опубликовал runwable MWE со всеми импортными данными here.

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

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

ответ

0

Когда вы обновляете существующую «базу данных», вы получаете 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) 
    #      ^^^^ ^
+0

Я попробовал ваше предложение, но, к сожалению, не похоже, чтобы сделать разницу. Если это может быть актуально, 'print (type (data))' перед вызовом 'yaml.dump' возвращает' '. – fndari

+0

Я предпочел бы сделать дамп YAML с 'stream = sys.stdout', чтобы вы могли видеть, что YAML думает, что он получает. – Anthon

+0

Я добавил ссылку на блокнот на мой вопрос с дампом 'sys.stdout'. – fndari