Make DownloadItem.reply private

This adds a new uses_nam method to DownloadItem though.
This commit is contained in:
Florian Bruhin 2016-09-11 20:48:28 +02:00
parent 36b7485262
commit 3550d59e3a

View File

@ -272,7 +272,6 @@ class DownloadItem(QObject):
autoclose: Whether to close the associated file if the download is autoclose: Whether to close the associated file if the download is
done. done.
fileobj: The file object to download the file to. fileobj: The file object to download the file to.
reply: The QNetworkReply associated with this download.
retry_info: A _RetryInfo instance. retry_info: A _RetryInfo instance.
raw_headers: The headers sent by the server. raw_headers: The headers sent by the server.
_filename: The filename of the download. _filename: The filename of the download.
@ -283,6 +282,7 @@ class DownloadItem(QObject):
periodically. periodically.
_win_id: The window ID the DownloadItem runs in. _win_id: The window ID the DownloadItem runs in.
_dead: Whether the Download has _die()'d. _dead: Whether the Download has _die()'d.
_reply: The QNetworkReply associated with this download.
Signals: Signals:
data_changed: The downloads metadata changed. data_changed: The downloads metadata changed.
@ -320,7 +320,7 @@ class DownloadItem(QObject):
self.stats = DownloadItemStats(self) self.stats = DownloadItemStats(self)
self.index = 0 self.index = 0
self.autoclose = True self.autoclose = True
self.reply = None self._reply = None
self._buffer = io.BytesIO() self._buffer = io.BytesIO()
self._read_timer = usertypes.Timer(self, name='download-read-timer') self._read_timer = usertypes.Timer(self, name='download-read-timer')
self._read_timer.setInterval(500) self._read_timer.setInterval(500)
@ -416,10 +416,10 @@ class DownloadItem(QObject):
return return
self._dead = True self._dead = True
self._read_timer.stop() self._read_timer.stop()
self.reply.downloadProgress.disconnect() self._reply.downloadProgress.disconnect()
self.reply.finished.disconnect() self._reply.finished.disconnect()
self.reply.error.disconnect() self._reply.error.disconnect()
self.reply.readyRead.disconnect() self._reply.readyRead.disconnect()
self.error_msg = msg self.error_msg = msg
self.stats.finish() self.stats.finish()
self.error.emit(msg) self.error.emit(msg)
@ -427,9 +427,9 @@ class DownloadItem(QObject):
'problem, this method must only be called ' 'problem, this method must only be called '
'once.'): 'once.'):
# See https://codereview.qt-project.org/#/c/107863/ # See https://codereview.qt-project.org/#/c/107863/
self.reply.abort() self._reply.abort()
self.reply.deleteLater() self._reply.deleteLater()
self.reply = None self._reply = None
self.done = True self.done = True
self.data_changed.emit() self.data_changed.emit()
if self.fileobj is not None: if self.fileobj is not None:
@ -446,14 +446,14 @@ class DownloadItem(QObject):
""" """
self.done = False self.done = False
self.successful = False self.successful = False
self.reply = reply self._reply = reply
reply.setReadBufferSize(16 * 1024 * 1024) # 16 MB reply.setReadBufferSize(16 * 1024 * 1024) # 16 MB
reply.downloadProgress.connect(self.stats.on_download_progress) reply.downloadProgress.connect(self.stats.on_download_progress)
reply.finished.connect(self.on_reply_finished) reply.finished.connect(self.on_reply_finished)
reply.error.connect(self.on_reply_error) reply.error.connect(self.on_reply_error)
reply.readyRead.connect(self.on_ready_read) reply.readyRead.connect(self.on_ready_read)
reply.metaDataChanged.connect(self.on_meta_data_changed) reply.metaDataChanged.connect(self.on_meta_data_changed)
self.retry_info = RetryInfo(request=reply.request(), self.retry_info = _RetryInfo(request=reply.request(),
manager=reply.manager()) manager=reply.manager())
if not self.fileobj: if not self.fileobj:
self._read_timer.start() self._read_timer.start()
@ -495,11 +495,11 @@ class DownloadItem(QObject):
log.downloads.debug("cancelled") log.downloads.debug("cancelled")
self._read_timer.stop() self._read_timer.stop()
self.cancelled.emit() self.cancelled.emit()
if self.reply is not None: if self._reply is not None:
self.reply.finished.disconnect(self.on_reply_finished) self._reply.finished.disconnect(self.on_reply_finished)
self.reply.abort() self._reply.abort()
self.reply.deleteLater() self._reply.deleteLater()
self.reply = None self._reply = None
if self.fileobj is not None: if self.fileobj is not None:
self.fileobj.close() self.fileobj.close()
if remove_data: if remove_data:
@ -642,7 +642,7 @@ class DownloadItem(QObject):
self._buffer.seek(0) self._buffer.seek(0)
shutil.copyfileobj(self._buffer, fileobj) shutil.copyfileobj(self._buffer, fileobj)
self._buffer.close() self._buffer.close()
if self.reply.isFinished(): if self._reply.isFinished():
# Downloading to the buffer in RAM has already finished so we # Downloading to the buffer in RAM has already finished so we
# write out the data and clean up now. # write out the data and clean up now.
self.on_reply_finished() self.on_reply_finished()
@ -657,14 +657,14 @@ class DownloadItem(QObject):
def finish_download(self): def finish_download(self):
"""Write buffered data to disk and finish the QNetworkReply.""" """Write buffered data to disk and finish the QNetworkReply."""
log.downloads.debug("Finishing download...") log.downloads.debug("Finishing download...")
if self.reply.isOpen(): if self._reply.isOpen():
self.fileobj.write(self.reply.readAll()) self.fileobj.write(self._reply.readAll())
if self.autoclose: if self.autoclose:
self.fileobj.close() self.fileobj.close()
self.successful = self.reply.error() == QNetworkReply.NoError self.successful = self._reply.error() == QNetworkReply.NoError
self.reply.close() self._reply.close()
self.reply.deleteLater() self._reply.deleteLater()
self.reply = None self._reply = None
self.finished.emit() self.finished.emit()
self.done = True self.done = True
log.downloads.debug("Download {} finished".format(self.basename)) log.downloads.debug("Download {} finished".format(self.basename))
@ -678,7 +678,7 @@ class DownloadItem(QObject):
doesn't mean the download (i.e. writing data to the disk) is finished doesn't mean the download (i.e. writing data to the disk) is finished
as well. Therefore, we can't close() the QNetworkReply in here yet. as well. Therefore, we can't close() the QNetworkReply in here yet.
""" """
if self.reply is None: if self._reply is None:
return return
self._read_timer.stop() self._read_timer.stop()
self.stats.finish() self.stats.finish()
@ -694,15 +694,15 @@ class DownloadItem(QObject):
@pyqtSlot() @pyqtSlot()
def on_ready_read(self): def on_ready_read(self):
"""Read available data and save file when ready to read.""" """Read available data and save file when ready to read."""
if self.fileobj is None or self.reply is None: if self.fileobj is None or self._reply is None:
# No filename has been set yet (so we don't empty the buffer) or we # No filename has been set yet (so we don't empty the buffer) or we
# got a readyRead after the reply was finished (which happens on # got a readyRead after the reply was finished (which happens on
# qute:log for example). # qute:log for example).
return return
if not self.reply.isOpen(): if not self._reply.isOpen():
raise OSError("Reply is closed!") raise OSError("Reply is closed!")
try: try:
self.fileobj.write(self.reply.readAll()) self.fileobj.write(self._reply.readAll())
except OSError as e: except OSError as e:
self._die(e.strerror) self._die(e.strerror)
@ -712,24 +712,24 @@ class DownloadItem(QObject):
if code == QNetworkReply.OperationCanceledError: if code == QNetworkReply.OperationCanceledError:
return return
else: else:
self._die(self.reply.errorString()) self._die(self._reply.errorString())
@pyqtSlot() @pyqtSlot()
def on_read_timer_timeout(self): def on_read_timer_timeout(self):
"""Read some bytes from the QNetworkReply periodically.""" """Read some bytes from the QNetworkReply periodically."""
if not self.reply.isOpen(): if not self._reply.isOpen():
raise OSError("Reply is closed!") raise OSError("Reply is closed!")
data = self.reply.read(1024) data = self._reply.read(1024)
if data is not None: if data is not None:
self._buffer.write(data) self._buffer.write(data)
@pyqtSlot() @pyqtSlot()
def on_meta_data_changed(self): def on_meta_data_changed(self):
"""Update the download's metadata.""" """Update the download's metadata."""
if self.reply is None: if self._reply is None:
return return
self.raw_headers = {} self.raw_headers = {}
for key, value in self.reply.rawHeaderPairs(): for key, value in self._reply.rawHeaderPairs():
self.raw_headers[bytes(key)] = bytes(value) self.raw_headers[bytes(key)] = bytes(value)
def _handle_redirect(self): def _handle_redirect(self):
@ -738,12 +738,12 @@ class DownloadItem(QObject):
Return: Return:
True if the download was redirected, False otherwise. True if the download was redirected, False otherwise.
""" """
redirect = self.reply.attribute( redirect = self._reply.attribute(
QNetworkRequest.RedirectionTargetAttribute) QNetworkRequest.RedirectionTargetAttribute)
if redirect is None or redirect.isEmpty(): if redirect is None or redirect.isEmpty():
return False return False
new_url = self.reply.url().resolved(redirect) new_url = self._reply.url().resolved(redirect)
request = self.reply.request() request = self._reply.request()
if new_url == request.url(): if new_url == request.url():
return False return False
@ -755,16 +755,23 @@ class DownloadItem(QObject):
log.downloads.debug("{}: Handling redirect".format(self)) log.downloads.debug("{}: Handling redirect".format(self))
self._redirects += 1 self._redirects += 1
request.setUrl(new_url) request.setUrl(new_url)
reply = self.reply reply = self._reply
reply.finished.disconnect(self.on_reply_finished) reply.finished.disconnect(self.on_reply_finished)
self._read_timer.stop() self._read_timer.stop()
self.reply = None self._reply = None
if self.fileobj is not None: if self.fileobj is not None:
self.fileobj.seek(0) self.fileobj.seek(0)
self.redirected.emit(request, reply) # this will change self.reply! self.redirected.emit(request, reply) # this will change self._reply!
reply.deleteLater() # the old one reply.deleteLater() # the old one
return True return True
def uses_nam(self, nam):
running_nam = self._reply is not None and self._reply.manager() is nam
# user could request retry after tab is closed.
retry_nam = (self.done and (not self.successful) and
self.retry_info.manager is nam)
return running_nam or retry_nam
class DownloadManager(QObject): class DownloadManager(QObject):
@ -1061,12 +1068,7 @@ class DownloadManager(QObject):
""" """
assert nam.adopted_downloads == 0 assert nam.adopted_downloads == 0
for download in self.downloads: for download in self.downloads:
running_download = (download.reply is not None and if download.uses_nam(nam):
download.reply.manager() is nam)
# user could request retry after tab is closed.
failed_download = (download.done and (not download.successful) and
download.retry_info.manager is nam)
if running_download or failed_download:
nam.adopt_download(download) nam.adopt_download(download)
return nam.adopted_downloads return nam.adopted_downloads