2017-09-13 21:46:39 +02:00
|
|
|
import logging
|
|
|
|
from io import BytesIO
|
|
|
|
from twisted.python.failure import Failure
|
2018-11-04 20:06:29 +01:00
|
|
|
from lbrynet.p2p.Error import DownloadCanceledError, InvalidDataError
|
|
|
|
from lbrynet.p2p.cryptoutils import get_lbry_hash_obj
|
2017-09-13 21:46:39 +02:00
|
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2018-07-22 00:34:59 +02:00
|
|
|
class HashBlobWriter:
|
2017-09-13 21:46:39 +02:00
|
|
|
def __init__(self, length_getter, finished_cb):
|
|
|
|
self.write_handle = BytesIO()
|
|
|
|
self.length_getter = length_getter
|
|
|
|
self.finished_cb = finished_cb
|
|
|
|
self.finished_cb_d = None
|
|
|
|
self._hashsum = get_lbry_hash_obj()
|
|
|
|
self.len_so_far = 0
|
|
|
|
|
2017-09-29 20:29:35 +02:00
|
|
|
def __del__(self):
|
|
|
|
if self.finished_cb_d is None:
|
2018-07-22 01:08:28 +02:00
|
|
|
log.warning("Garbage collection was called, but writer was not closed yet")
|
2017-09-29 20:29:35 +02:00
|
|
|
self.close()
|
|
|
|
|
2017-09-13 21:46:39 +02:00
|
|
|
@property
|
|
|
|
def blob_hash(self):
|
|
|
|
return self._hashsum.hexdigest()
|
|
|
|
|
|
|
|
def write(self, data):
|
|
|
|
if self.write_handle is None:
|
2018-07-27 03:49:35 +02:00
|
|
|
log.warning("writer has already been closed")
|
2017-09-15 19:46:38 +02:00
|
|
|
raise IOError('I/O operation on closed file')
|
2017-09-13 21:46:39 +02:00
|
|
|
|
|
|
|
self._hashsum.update(data)
|
|
|
|
self.len_so_far += len(data)
|
|
|
|
if self.len_so_far > self.length_getter():
|
|
|
|
self.finished_cb_d = self.finished_cb(
|
|
|
|
self,
|
|
|
|
Failure(InvalidDataError("Length so far is greater than the expected length."
|
|
|
|
" %s to %s" % (self.len_so_far,
|
|
|
|
self.length_getter()))))
|
|
|
|
else:
|
|
|
|
self.write_handle.write(data)
|
|
|
|
if self.len_so_far == self.length_getter():
|
|
|
|
self.finished_cb_d = self.finished_cb(self)
|
|
|
|
|
|
|
|
def close_handle(self):
|
|
|
|
if self.write_handle is not None:
|
|
|
|
self.write_handle.close()
|
|
|
|
self.write_handle = None
|
|
|
|
|
|
|
|
def close(self, reason=None):
|
|
|
|
# if we've already called finished_cb because we either finished writing
|
|
|
|
# or closed already, do nothing
|
|
|
|
if self.finished_cb_d is not None:
|
|
|
|
return
|
|
|
|
if reason is None:
|
|
|
|
reason = Failure(DownloadCanceledError())
|
|
|
|
self.finished_cb_d = self.finished_cb(self, reason)
|