使用ruamel.yaml转储为字符串的最佳方式不是流

使用ruamel.yaml转储为字符串的最佳方式不是流

问题描述:

过去,我使用ruamel.yaml的向后兼容部分做了类似some_fancy_printing_loggin_func(yaml.dump(...), ...)的工作,但我想将我的代码转换为使用最新的API这样我就可以利用一些新的格式设置。使用ruamel.yaml转储为字符串的最佳方式不是流

但是,我讨厌我必须指定流到ruamel.yaml.YAML.dump() ......我不希望它直接写入流;我只是希望它将输出返回给调用者。我错过了什么? PS:我知道我可以做类似以下的事情,尽管当然我试图避免它。

f = io.StringIO() 
yml.dump(myobj, f) 
f.seek(0) 
my_logging_func(f.read()) 

我不知道,如果你真的失去了一些东西,如果在所有它可能是,如果你与流工作,你应该 - 最好-继续流工作。这不过是一些ruamel.yaml和PyYAML的许多用户似乎没有意识到,所以他们做的:

print(dump(data)) 

代替

dump(data, sys.stdout) 

前者可能是罚款,在使用非真实数据(PyYAML)文档,但会导致真实数据的不良习惯。

最好的解决方案是使您的my_logging_func()流为导向。这可以例如完成如下:

import sys 
import ruamel.yaml 

data = dict(user='rsaw', question=47614862) 

class MyLogger: 
    def write(self, s): 
     sys.stdout.write(s.decode('utf-8')) 

my_logging_func = MyLogger() 
yml = ruamel.yaml.YAML() 
yml.dump(data, my_logging_func) 

这给:

user: rsaw 
question: 47614862 

但要注意,MyLogger.write()被多次调用(在这种情况下八次),如果你需要在时间上线工作,你必须做线路缓冲。

尽管您可以包装非流式接口,但这并不总是很方便。在ruamel.yaml<=0.15.34中,您可以滥用transform参数。这个参数需要一个函数,它传递给YAML文档的一个完整的字符串表示(与老API返回一个字符串一样低效,所以要小心),哪个应该返回一个转换后的字符串,然后进行流式处理。如果您将流设置为接收器,则不必关心该函数的返回值:

import sys 
import ruamel.yaml 

data = dict(user='rsaw', question=47614862) 

def my_logging_func(s): 
    print(s, end='') 

class NullStream: 
    def write(self, s): 
     pass 

yml = ruamel.yaml.YAML() 
yml.dump(data, NullStream(), transform=my_logging_func) 

获得相同的输出。

随着ruamel.yaml>0.15.34,假设你提供transform参数,可以提供None作为流:

import sys 
import ruamel.yaml 

data = dict(user='rsaw', question=47614862) 

def my_logging_func(s): 
    print(s, end='') 

yml = ruamel.yaml.YAML() 
yml.dump(data, None, transform=my_logging_func) 

而且,你甚至可以滥用更多的事情要做:

import ruamel.yaml 

data = dict(user='rsaw', question=47614862) 

yml = ruamel.yaml.YAML() 
yml.dump(data, None, transform=print) 

(但它会在输出结束时为你提供额外的换行符,就像旧的print(dump(data))一样)。

+0

你是我的英雄安东。感谢您的详细解释,并感谢您对ruamel.yaml所做的所有重要工作。 – rsaw