Skip to content

Commit 667ef0c

Browse files
committed
Make it possible to dump to byte streams
1 parent 13bf983 commit 667ef0c

File tree

3 files changed

+46
-6
lines changed

3 files changed

+46
-6
lines changed

README.rst

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,14 +187,23 @@ Unicode
187187
-------
188188

189189
Redis operates on bytes and has no concept of Unicode or encodings.
190-
JSON operates on Unicode strings and cannot serialize binary data. Therefore,
190+
JSON operates on (Unicode) strings and cannot serialize binary data. Therefore,
191191
redis-dump-load has to encode Unicode strings into byte strings when
192192
loading data into Redis and decode byte strings into Unicode strings
193193
when dumping data from Redis.
194-
By default redis-dump-load uses utf-8 for encoding and decoding.
194+
By default redis-dump-load uses utf-8 for encoding data sent to Redis
195+
and decoding data received from Redis.
195196
This behavior matches redis-py, whose default encoding is utf-8.
196197
A different encoding can be specified.
197198

199+
``dumps`` returns strings, that is, instances of ``str`` on Python 2
200+
and instances of ``unicode`` on Python 3.
201+
202+
When dumping to an IO object using ``dump``, and the IO object accepts
203+
byte strings (such as when a file is opened in binary mode),
204+
redis-dump-load will ``.encode()`` the dumped data using the default
205+
encoding in effect.
206+
198207
ijson's yajl2 backend can only decode ``bytes`` instances, not ``str``.
199208
When loading data from a file opened in text mode and using ijson-yajl2,
200209
redis-dump-load will encode the file data using utf-8 encoding before

redisdl.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,21 @@ def dumps(host='localhost', port=6379, password=None, db=0, pretty=False,
138138
subd['expireat'] = _time.time() + ttl
139139
return encoder.encode(table)
140140

141+
class BytesWriteWrapper(object):
142+
def __init__(self, stream):
143+
self.stream = stream
144+
145+
def write(self, str):
146+
return self.stream.write(str.encode())
147+
141148
def dump(fp, host='localhost', port=6379, password=None, db=0, pretty=False,
142149
unix_socket_path=None, encoding='utf-8', keys='*'):
150+
151+
try:
152+
fp.write('')
153+
except TypeError:
154+
fp = BytesWriteWrapper(fp)
155+
143156
if pretty:
144157
# hack to avoid implementing pretty printing
145158
fp.write(dumps(host=host, port=port, password=password, db=db,
@@ -356,14 +369,14 @@ def ijson_top_level_items(file, local_streaming_backend):
356369
except StopIteration:
357370
pass
358371

359-
class TextWrapper(object):
372+
class TextReadWrapper(object):
360373
def __init__(self, fp):
361374
self.fp = fp
362375

363376
def read(self, *args, **kwargs):
364377
return self.fp.read(*args, **kwargs).decode()
365378

366-
class BytesWrapper(object):
379+
class BytesReadWrapper(object):
367380
def __init__(self, fp):
368381
self.fp = fp
369382

@@ -394,15 +407,15 @@ def create_loader(fp, streaming_backend=None):
394407
if not have_ijson:
395408
raise TypeError('%s backend requested but ijson is not present' % streaming_backend)
396409
if py3 and isinstance(fp.read(0), str):
397-
fp = BytesWrapper(fp)
410+
fp = BytesReadWrapper(fp)
398411
def loader():
399412
return ijson_top_level_items(fp, option)
400413
else:
401414
if not have_jsaone:
402415
raise TypeError('jsaone backend requested but jsaone is not present')
403416
if py3 and isinstance(fp.read(0), bytes):
404417
# jsaone can only process text string data (str), not bytes
405-
fp = TextWrapper(fp)
418+
fp = TextReadWrapper(fp)
406419
def loader():
407420
return jsaone_mod.load(fp)
408421

tests/module_test.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,3 +279,21 @@ def test_load_expireat_preference(self):
279279
redisdl.loads(dump, use_expireat=True)
280280
ttl = self.r.ttl('key')
281281
self.assertGreater(ttl, 36000)
282+
283+
def test_dump_to_stringio(self):
284+
self.r.set('a', 'aaa')
285+
286+
fp = StringIO()
287+
redisdl.dump(fp, keys='a')
288+
actual = json.loads(fp.getvalue())
289+
290+
self.assertEqual(actual['a']['value'], 'aaa')
291+
292+
def test_dump_to_bytesio(self):
293+
self.r.set('a', 'aaa')
294+
295+
fp = BytesIO()
296+
redisdl.dump(fp, keys='a')
297+
actual = json.loads(fp.getvalue().decode())
298+
299+
self.assertEqual(actual['a']['value'], 'aaa')

0 commit comments

Comments
 (0)