Implement support for handling file objects as

input to the freeze function, fixes #79.
This commit is contained in:
Friedrich Lindenberg 2014-05-12 19:35:44 +02:00
parent 9c32d18607
commit d56b767479
4 changed files with 40 additions and 11 deletions

View File

@ -20,9 +20,9 @@ parser.add_argument('--db', default=None,
help='Override the freezefile database URI')
def freeze(result, format='csv', filename='freeze.csv',
prefix='.', meta={}, indent=2, mode='list',
wrap=True, callback=None, **kw):
def freeze(result, format='csv', filename='freeze.csv', fileobj=None,
prefix='.', meta={}, indent=2, mode='list', wrap=True,
callback=None, **kw):
"""
Perform a data export of a given result set. This is a very
flexible exporter, allowing for various output formats, metadata
@ -34,6 +34,15 @@ def freeze(result, format='csv', filename='freeze.csv',
result = db['person'].all()
dataset.freeze(result, format='json', filename='all-persons.json')
Instead of passing in the file name, you can also pass a file object::
result = db['person'].all()
fh = open('/dev/null', 'wb')
dataset.freeze(result, format='json', fileobj=fh)
Be aware that this will disable file name templating and store all
results to the same file.
If ``result`` is a table (rather than a result set), all records in
the table are exported (as if ``result.all()`` had been called).
@ -50,7 +59,8 @@ def freeze(result, format='csv', filename='freeze.csv',
is set to *item* the function would generate one file per row. In
that case you can use values as placeholders in filenames::
dataset.freeze(res, mode='item', format='json', filename='item-{{id}}.json')
dataset.freeze(res, mode='item', format='json',
filename='item-{{id}}.json')
The following output ``format`` s are supported:
@ -70,6 +80,7 @@ def freeze(result, format='csv', filename='freeze.csv',
kw.update({
'format': format,
'filename': filename,
'fileobj': fileobj,
'prefix': prefix,
'meta': meta,
'indent': indent,

View File

@ -1,5 +1,6 @@
import os
import re
import sys
import locale
try:
@ -28,8 +29,12 @@ class Serializer(object):
self._paths = []
self._get_basepath()
if export.get('filename') == '-':
export['fileobj'] = sys.stdout
self.fileobj = export.get('fileobj')
def _get_basepath(self):
prefix = self.export.get('prefix')
prefix = self.export.get('prefix', '')
prefix = os.path.abspath(prefix)
prefix = os.path.realpath(prefix)
self._prefix = prefix
@ -49,6 +54,10 @@ class Serializer(object):
return os.path.realpath(path.encode(enc, 'replace'))
def file_name(self, row):
# signal that there is a fileobj available:
if self.fileobj is not None:
return None
path = self._tmpl(row)
if path not in self._paths:
if not path.startswith(self._prefix):
@ -79,4 +88,5 @@ class Serializer(object):
row[field] = OPERATIONS.get(operation)(row.get(field))
self.write(self.file_name(row), row)
self.close()

View File

@ -22,7 +22,13 @@ class CSVSerializer(Serializer):
def write(self, path, result):
keys = list(result.keys())
if path not in self.handles:
fh = open(path, 'wb')
# handle fileobj that has been passed in:
if path is not None:
fh = open(path, 'wb')
else:
fh = self.fileobj
writer = csv.writer(fh)
writer.writerow([k.encode('utf-8') for k in keys])
self.handles[path] = (writer, fh)

View File

@ -36,13 +36,15 @@ class JSONSerializer(Serializer):
def close(self):
for path, result in self.buckets.items():
result = self.wrap(result)
fh = open(path, 'wb')
fh = open(path, 'wb') if self.fileobj is None else self.fileobj
data = json.dumps(result,
cls=JSONEncoder,
indent=self.export.get_int('indent'))
cls=JSONEncoder,
indent=self.export.get_int('indent'))
if self.export.get('callback'):
data = "%s && %s(%s);" % (self.export.get('callback'),
self.export.get('callback'),
data)
self.export.get('callback'),
data)
fh.write(data)
fh.close()