/
sbt_storage.py
156 lines (111 loc) · 3.99 KB
/
sbt_storage.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
from __future__ import print_function, unicode_literals, division
import abc
from io import BytesIO
import os
import tarfile
class Storage(abc.ABCMeta(str('ABC'), (object,), {'__slots__': ()})):
# this weird baseclass is compatible with Python 2 *and* 3,
# we can remove once we support only py3.4+
@abc.abstractmethod
def save(self, path, content):
pass
@abc.abstractmethod
def load(self, path):
pass
def init_args(self):
return {}
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
pass
class FSStorage(Storage):
def __init__(self, location, subdir):
self.location = location
self.subdir = subdir
fullpath = os.path.join(location, subdir)
if not os.path.exists(fullpath):
os.makedirs(fullpath)
def init_args(self):
return {'path': self.subdir}
def save(self, path, content):
"Save a node/leaf."
with open(os.path.join(self.location, self.subdir, path), 'wb') as f:
f.write(content)
return path
def load(self, path):
out = BytesIO()
with open(os.path.join(self.location, self.subdir, path), 'rb') as f:
out.write(f.read())
return out.getvalue()
class TarStorage(Storage):
def __init__(self, path=None):
# TODO: leave it open, or close/open every time?
if path is None:
# TODO: Open a temporary file?
pass
self.path = os.path.abspath(path)
dirname = os.path.dirname(self.path)
if not os.path.exists(dirname):
os.makedirs(dirname)
if os.path.exists(self.path):
self.tarfile = tarfile.open(path, 'r')
else:
self.tarfile = tarfile.open(path, 'w:gz')
def save(self, path, content):
info = tarfile.TarInfo(path)
info.size = len(content)
# TODO: check tarfile mode, if read-only reopen as writable
self.tarfile.addfile(info, BytesIO(content))
return path
def load(self, path):
content = self.tarfile.getmember(path)
f = self.tarfile.extractfile(content)
return f.read()
def init_args(self):
return {'path': self.path}
def __exit__(self, type, value, traceback):
self.tarfile.close()
class IPFSStorage(Storage):
def __init__(self, pin_on_add=True, **kwargs):
import ipfsapi
self.ipfs_args = kwargs
self.pin_on_add = pin_on_add
self.api = ipfsapi.connect(**self.ipfs_args)
def save(self, path, content):
# api.add_bytes(b"Mary had a little lamb")
new_obj = self.api.add_bytes(content)
if self.pin_on_add:
self.api.pin_add(new_obj)
return new_obj
# TODO: the above solution is quick and dirty.
# we actually want something more organized,
# like putting all the generated objects inside the same dir.
# Check this call using the files API for an example.
# api.files_write("/test/file", io.BytesIO(b"hi"), create=True)
def load(self, path):
return self.api.cat(path)
def init_args(self):
return self.ipfs_args
def __exit__(self, type, value, traceback):
# TODO: do nothing for now,
# but we actually want something more organized,
# like putting all the generated objects inside the same dir.
# Use the files API,
# add files without flush(),
# and then flush it here?
pass
class RedisStorage(Storage):
def __init__(self, **kwargs):
import redis
self.redis_args = kwargs
self.conn = redis.Redis(**self.redis_args)
def save(self, path, content):
self.conn.set(path, content)
return path
def load(self, path):
return self.conn.get(path)
def init_args(self):
# TODO: do we want to remove stuff like password from here?
return self.redis_args
def __exit__(self, type, value, traceback):
pass