Skip to content

Commit 19597e5

Browse files
committed
Added base64 IO support.
1 parent 30c5945 commit 19597e5

7 files changed

Lines changed: 159 additions & 0 deletions

File tree

benedict/dicts/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ def flatten(self, separator='_'):
5353
def fromkeys(cls, sequence, value=None):
5454
return KeypathDict.fromkeys(sequence, value)
5555

56+
@staticmethod
57+
@benediction
58+
def from_base64(s, **kwargs):
59+
return IODict.from_base64(s, **kwargs)
60+
5661
@staticmethod
5762
@benediction
5863
def from_json(s, **kwargs):

benedict/dicts/io.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ def _encode(d, encoder, filepath=None, **kwargs):
4949

5050
@staticmethod
5151
def _from_any_data_string(s, **kwargs):
52+
try:
53+
d = IODict.from_base64(s, **kwargs)
54+
return d
55+
except ValueError:
56+
pass
5257
try:
5358
d = IODict.from_json(s, **kwargs)
5459
return d
@@ -70,6 +75,11 @@ def _from_any_data_string(s, **kwargs):
7075
except ValueError:
7176
pass
7277

78+
@staticmethod
79+
def from_base64(s, **kwargs):
80+
return IODict._decode(s,
81+
decoder=io_util.decode_base64, **kwargs)
82+
7383
@staticmethod
7484
def from_json(s, **kwargs):
7585
return IODict._decode(s,
@@ -90,6 +100,11 @@ def from_yaml(cls, s, **kwargs):
90100
return IODict._decode(s,
91101
decoder=io_util.decode_yaml, **kwargs)
92102

103+
def to_base64(self, filepath=None, **kwargs):
104+
return IODict._encode(self,
105+
encoder=io_util.encode_base64,
106+
filepath=filepath, **kwargs)
107+
93108
def to_json(self, filepath=None, **kwargs):
94109
return IODict._encode(self,
95110
encoder=io_util.encode_json,

benedict/utils/io_util.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# -*- coding: utf-8 -*-
22

3+
import base64
34
import errno
45
import json
56
import os
@@ -8,6 +9,12 @@
89
import toml
910
import yaml
1011

12+
def decode_base64(s, **kwargs):
13+
decode_func = kwargs.pop('through', decode_json)
14+
b = base64.b64decode(s)
15+
s = b.decode('utf-8')
16+
return decode_func(s, **kwargs)
17+
1118

1219
def decode_json(s, **kwargs):
1320
data = json.loads(s, **kwargs)
@@ -31,6 +38,13 @@ def decode_yaml(s, **kwargs):
3138
return data
3239

3340

41+
def encode_base64(d, **kwargs):
42+
encode_func = kwargs.pop('through', encode_json)
43+
data = base64.b64encode(
44+
encode_func(d, **kwargs).encode('utf-8')).decode('utf-8')
45+
return data
46+
47+
3448
def encode_json(d, **kwargs):
3549
data = json.dumps(d, **kwargs)
3650
return data

tests/input/invalid-content.base64

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Lorem ipsum consectetur sint id aute officia sed excepteur consectetur labore laboris dolore in labore consequat ut in eu ut deserunt.
2+
Elit aliqua velit aliquip voluptate consequat reprehenderit occaecat dolor ut esse aute laboris cillum fugiat esse est laborum.

tests/input/valid-content.base64

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
eyJhIjogMSwgImIiOiAyLCAiYyI6IDN9

tests/test_benedict.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,17 @@ def test_fromkeys_with_value(self):
455455
self.assertEqual(b, r)
456456
self.assertEqual(type(b), benedict)
457457

458+
def test_from_base64(self):
459+
j = 'eyJhIjogMSwgImIiOiAyLCAiYyI6IDN9'
460+
# static method
461+
d = benedict.from_base64(j)
462+
self.assertTrue(isinstance(d, benedict))
463+
self.assertEqual(d, { 'a': 1, 'b': 2, 'c': 3, })
464+
# constructor
465+
d = benedict(j)
466+
self.assertTrue(isinstance(d, benedict))
467+
self.assertEqual(d, { 'a': 1, 'b': 2, 'c': 3, })
468+
458469
def test_from_json(self):
459470
j = '{"a": 1, "b": 2, "c": 3}'
460471
# static method

tests/test_io_dict.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,117 @@ def output_path(filepath):
2323
dir_path = os.path.dirname(os.path.realpath(__file__))
2424
return os.path.join(dir_path, 'output/{}'.format(filepath))
2525

26+
# BASE64
27+
28+
def test_from_base64_with_valid_data(self):
29+
j = 'eyJhIjogMSwgImIiOiAyLCAiYyI6IDN9'
30+
# j = '{"a": 1, "b": 2, "c": 3}'
31+
# static method
32+
d = IODict.from_base64(j)
33+
self.assertTrue(isinstance(d, dict))
34+
self.assertEqual(d, { 'a': 1, 'b': 2, 'c': 3, })
35+
# constructor
36+
d = IODict(j)
37+
self.assertTrue(isinstance(d, dict))
38+
self.assertEqual(d, { 'a': 1, 'b': 2, 'c': 3, })
39+
40+
def test_from_base64_with_invalid_data(self):
41+
j = 'Lorem ipsum est in ea occaecat nisi officia.'
42+
# static method
43+
with self.assertRaises(ValueError):
44+
d = IODict.from_base64(j)
45+
# constructor
46+
with self.assertRaises(ValueError):
47+
d = IODict(j)
48+
49+
def test_from_base64_with_valid_file_valid_content(self):
50+
filepath = self.input_path('valid-content.base64')
51+
# static method
52+
d = IODict.from_base64(filepath)
53+
self.assertTrue(isinstance(d, dict))
54+
# constructor
55+
d = IODict(filepath)
56+
self.assertTrue(isinstance(d, dict))
57+
58+
def test_from_base64_with_valid_file_valid_content_invalid_format(self):
59+
filepath = self.input_path('valid-content.json')
60+
with self.assertRaises(ValueError):
61+
d = IODict.from_base64(filepath)
62+
filepath = self.input_path('valid-content.toml')
63+
with self.assertRaises(ValueError):
64+
d = IODict.from_base64(filepath)
65+
filepath = self.input_path('valid-content.xml')
66+
with self.assertRaises(ValueError):
67+
d = IODict.from_base64(filepath)
68+
filepath = self.input_path('valid-content.yml')
69+
with self.assertRaises(ValueError):
70+
d = IODict.from_base64(filepath)
71+
72+
def test_from_base64_with_valid_file_invalid_content(self):
73+
filepath = self.input_path('invalid-content.base64')
74+
# static method
75+
with self.assertRaises(ValueError):
76+
d = IODict.from_base64(filepath)
77+
# constructor
78+
with self.assertRaises(ValueError):
79+
d = IODict(filepath)
80+
81+
def test_from_base64_with_invalid_file(self):
82+
filepath = self.input_path('invalid-file.base64')
83+
# static method
84+
with self.assertRaises(ValueError):
85+
d = IODict.from_base64(filepath)
86+
# constructor
87+
with self.assertRaises(ValueError):
88+
d = IODict(filepath)
89+
90+
# def test_from_base64_with_valid_url_valid_content(self):
91+
# url = 'https://raw.githubusercontent.com/fabiocaccamo/python-benedict/master/tests/input/valid-content.base64'
92+
# # static method
93+
# d = IODict.from_base64(url)
94+
# self.assertTrue(isinstance(d, dict))
95+
# # constructor
96+
# d = IODict(url)
97+
# self.assertTrue(isinstance(d, dict))
98+
99+
def test_from_base64_with_valid_url_invalid_content(self):
100+
url = 'https://github.com/fabiocaccamo/python-benedict'
101+
# static method
102+
with self.assertRaises(ValueError):
103+
d = IODict.from_base64(url)
104+
# constructor
105+
with self.assertRaises(ValueError):
106+
d = IODict(url)
107+
108+
def test_from_base64_with_invalid_url(self):
109+
url = 'https://github.com/fabiocaccamo/python-benedict-invalid'
110+
# static method
111+
with self.assertRaises(ValueError):
112+
d = IODict.from_base64(url)
113+
# constructor
114+
with self.assertRaises(ValueError):
115+
d = IODict(url)
116+
117+
def test_to_base64(self):
118+
d = IODict({
119+
'a': 1,
120+
'b': 2,
121+
'c': 3,
122+
})
123+
s = d.to_base64(sort_keys=True)
124+
self.assertEqual(s, 'eyJhIjogMSwgImIiOiAyLCAiYyI6IDN9')
125+
126+
def test_to_base64_file(self):
127+
d = IODict({
128+
'a': 1,
129+
'b': 2,
130+
'c': 3,
131+
})
132+
filepath = self.output_path('test_to_base64_file.base64')
133+
s = d.to_base64(filepath=filepath, sort_keys=True)
134+
self.assertTrue(d, os.path.isfile(filepath))
135+
self.assertEqual(d, IODict.from_base64(filepath))
136+
26137
# JSON
27138

28139
def test_from_json_with_valid_data(self):

0 commit comments

Comments
 (0)