From 9381aac501a3ad8c108e052dc04b200701f501a8 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 13 Apr 2015 20:42:28 +0200 Subject: [PATCH] Merge {Pastebin,PyPIVersion}Client into HTTPClient. --- qutebrowser/browser/network/pastebin.py | 48 ++++------- qutebrowser/misc/autoupdate.py | 41 +++------- qutebrowser/misc/httpclient.py | 104 ++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 60 deletions(-) create mode 100644 qutebrowser/misc/httpclient.py diff --git a/qutebrowser/browser/network/pastebin.py b/qutebrowser/browser/network/pastebin.py index 1ab8454f8..af05256f2 100644 --- a/qutebrowser/browser/network/pastebin.py +++ b/qutebrowser/browser/network/pastebin.py @@ -19,21 +19,19 @@ """Client for the pastebin.""" -import functools -import urllib.request import urllib.parse -from PyQt5.QtCore import pyqtSignal, QObject, QUrl -from PyQt5.QtNetwork import (QNetworkAccessManager, QNetworkRequest, - QNetworkReply) +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl + +from qutebrowser.misc import httpclient class PastebinClient(QObject): - """A client for http://p.cmpl.cc/ using QNetworkAccessManager. + """A client for http://p.cmpl.cc/ using HTTPClient. Attributes: - _nam: The QNetworkAccessManager used. + _client: The HTTPClient used. Class attributes: API_URL: The base API URL. @@ -51,7 +49,9 @@ class PastebinClient(QObject): def __init__(self, parent=None): super().__init__(parent) - self._nam = QNetworkAccessManager(self) + self._client = httpclient.HTTPClient(self) + self._client.error.connect(self.error) + self._client.success.connect(self.on_client_success) def paste(self, name, title, text, parent=None): """Paste the text into a pastebin and return the URL. @@ -69,33 +69,17 @@ class PastebinClient(QObject): } if parent is not None: data['reply'] = parent - encoded_data = urllib.parse.urlencode(data).encode('utf-8') - create_url = urllib.parse.urljoin(self.API_URL, 'create') - request = QNetworkRequest(QUrl(create_url)) - request.setHeader(QNetworkRequest.ContentTypeHeader, - 'application/x-www-form-urlencoded;charset=utf-8') - reply = self._nam.post(request, encoded_data) - if reply.isFinished(): - self.on_reply_finished(reply) - else: - reply.finished.connect(functools.partial( - self.on_reply_finished, reply)) + url = QUrl(urllib.parse.urljoin(self.API_URL, 'create')) + self._client.post(url, data) - def on_reply_finished(self, reply): - """Read the data and finish when the reply finished. + @pyqtSlot(str) + def on_client_success(self, data): + """Process the data and finish when the client finished. Args: - reply: The QNetworkReply which finished. + data: A string with the received data. """ - if reply.error() != QNetworkReply.NoError: - self.error.emit(reply.errorString()) - return - try: - url = bytes(reply.readAll()).decode('utf-8') - except UnicodeDecodeError: - self.error.emit("Invalid UTF-8 data received in reply!") - return - if url.startswith('http://'): - self.success.emit(url) + if data.startswith('http://'): + self.success.emit(data) else: self.error.emit("Invalid data received in reply!") diff --git a/qutebrowser/misc/autoupdate.py b/qutebrowser/misc/autoupdate.py index 370de0f06..6ea150300 100644 --- a/qutebrowser/misc/autoupdate.py +++ b/qutebrowser/misc/autoupdate.py @@ -20,21 +20,20 @@ """Classes related to auto-updating and getting the latest version.""" import json -import functools -from PyQt5.QtCore import pyqtSignal, QObject, QUrl -from PyQt5.QtNetwork import (QNetworkAccessManager, QNetworkRequest, - QNetworkReply) +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QUrl + +from qutebrowser.misc import httpclient class PyPIVersionClient(QObject): - """A client for the PyPI API using QNetworkAccessManager. + """A client for the PyPI API using HTTPClient. It gets the latest version of qutebrowser from PyPI. Attributes: - _nam: The QNetworkAccessManager used. + _client: The HTTPClient used. Class attributes: API_URL: The base API URL. @@ -52,7 +51,9 @@ class PyPIVersionClient(QObject): def __init__(self, parent=None): super().__init__(parent) - self._nam = QNetworkAccessManager(self) + self._client = httpclient.HTTPClient(self) + self._client.error.connect(self.error) + self._client.success.connect(self.on_client_success) def get_version(self, package='qutebrowser'): """Get the newest version of a given package. @@ -63,31 +64,15 @@ class PyPIVersionClient(QObject): package: The name of the package to check. """ url = QUrl(self.API_URL.format(package)) - request = QNetworkRequest(url) - reply = self._nam.get(request) - if reply.isFinished(): - self.on_reply_finished(reply) - else: - reply.finished.connect(functools.partial( - self.on_reply_finished, reply)) + self._client.get(url) - def on_reply_finished(self, reply): - """When the reply finished, load and parse the json data. - - Then emits error/success. + @pyqtSlot(str) + def on_client_success(self, data): + """Process the data and finish when the client finished. Args: - reply: The QNetworkReply which finished. + data: A string with the received data. """ - if reply.error() != QNetworkReply.NoError: - self.error.emit(reply.errorString()) - return - try: - data = bytes(reply.readAll()).decode('utf-8') - except UnicodeDecodeError as e: - self.error.emit("Invalid UTF-8 data received in reply: " - "{}!".format(e)) - return try: json_data = json.loads(data) except ValueError as e: diff --git a/qutebrowser/misc/httpclient.py b/qutebrowser/misc/httpclient.py new file mode 100644 index 000000000..90ead1d57 --- /dev/null +++ b/qutebrowser/misc/httpclient.py @@ -0,0 +1,104 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2015 Florian Bruhin (The Compiler) +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see . + +"""A HTTP client based on QNetworkAccessManager.""" + +import functools +import urllib.request +import urllib.parse + +from PyQt5.QtCore import pyqtSignal, QObject +from PyQt5.QtNetwork import (QNetworkAccessManager, QNetworkRequest, + QNetworkReply) + + +class HTTPClient(QObject): + + """A HTTP client based on QNetworkAccessManager. + + Intended for APIs, automatically decodes data. + + Attributes: + _nam: The QNetworkAccessManager used. + + Signals: + success: Emitted when the operation succeeded. + arg: The recieved data. + error: Emitted when the request failed. + arg: The error message, as string. + """ + + success = pyqtSignal(str) + error = pyqtSignal(str) + + def __init__(self, parent=None): + super().__init__(parent) + self._nam = QNetworkAccessManager(self) + + def post(self, url, data=None): + """Create a new POST request. + + Args: + url: The URL to post to, as QUrl. + data: A dict of data to send. + """ + if data is None: + data = {} + encoded_data = urllib.parse.urlencode(data).encode('utf-8') + request = QNetworkRequest(url) + request.setHeader(QNetworkRequest.ContentTypeHeader, + 'application/x-www-form-urlencoded;charset=utf-8') + reply = self._nam.post(request, encoded_data) + if reply.isFinished(): + self.on_reply_finished(reply) + else: + reply.finished.connect(functools.partial( + self.on_reply_finished, reply)) + + def get(self, url): + """Create a new GET request. + + Emits success/error when done. + + Args: + url: The URL to access, as QUrl. + """ + request = QNetworkRequest(url) + reply = self._nam.get(request) + if reply.isFinished(): + self.on_reply_finished(reply) + else: + reply.finished.connect(functools.partial( + self.on_reply_finished, reply)) + + def on_reply_finished(self, reply): + """Read the data and finish when the reply finished. + + Args: + reply: The QNetworkReply which finished. + """ + if reply.error() != QNetworkReply.NoError: + self.error.emit(reply.errorString()) + return + try: + data = bytes(reply.readAll()).decode('utf-8') + except UnicodeDecodeError: + self.error.emit("Invalid UTF-8 data received in reply!") + return + self.success.emit(data)