From 9bde00a1044c4e059553dc7445e15d5d60c32cf4 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Thu, 28 Jul 2022 15:29:38 -0500 Subject: [PATCH] Addon Manager: pylint cleanup of NetworkManager --- src/Mod/AddonManager/NetworkManager.py | 205 +++++++++++++++---------- 1 file changed, 123 insertions(+), 82 deletions(-) diff --git a/src/Mod/AddonManager/NetworkManager.py b/src/Mod/AddonManager/NetworkManager.py index 7a4dd322a2..2382eb9515 100644 --- a/src/Mod/AddonManager/NetworkManager.py +++ b/src/Mod/AddonManager/NetworkManager.py @@ -22,7 +22,7 @@ # * * # *************************************************************************** - +""" ############################################################################# # # ABOUT NETWORK MANAGER @@ -52,7 +52,15 @@ # accesses: the blocking_get() function blocks until the network transmission # is complete, directly returning a QByteArray object with the received data. # Do not run on the main GUI thread! +""" +import threading +import os +import queue +import itertools +import tempfile +import sys +from typing import Dict, List, Optional try: import FreeCAD @@ -62,18 +70,14 @@ try: HAVE_FREECAD = True translate = FreeCAD.Qt.translate -except Exception: +except ImportError: # For standalone testing support working without the FreeCAD import HAVE_FREECAD = False -import threading from PySide2 import QtCore +if FreeCAD.GuiUp: + from PySide2 import QtWidgets -import os -import queue -import itertools -import tempfile -from typing import Dict, List, Optional # This is the global instance of the NetworkManager that outside code # should access @@ -82,7 +86,7 @@ AM_NETWORK_MANAGER = None HAVE_QTNETWORK = True try: from PySide2 import QtNetwork -except Exception: +except ImportError: if HAVE_FREECAD: FreeCAD.Console.PrintError( translate( @@ -95,12 +99,14 @@ except Exception: print( "Could not import QtNetwork, unable to test this file. Try installing the python3-pyside2.qtnetwork package." ) - exit(1) + sys.exit(1) HAVE_QTNETWORK = False if HAVE_QTNETWORK: class QueueItem: + """A container for information about an item in the network queue.""" + def __init__( self, index: int, request: QtNetwork.QNetworkRequest, track_progress: bool ): @@ -164,68 +170,22 @@ if HAVE_QTNETWORK: self.QNAM.setCache(self.diskCache) self.monitored_connections: List[int] = [] + self._setup_proxy() + + # A helper connection for our blocking interface + self.completed.connect(self.__synchronous_process_completion) + + # Set up our worker connection + self.__request_queued.connect(self.__setup_network_request) + + def _setup_proxy(self): + """ Set up the proxy based on user preferences or prompts on command line """ # Set up the proxy, if necesssary: - noProxyCheck = True - systemProxyCheck = False - userProxyCheck = False - proxy_string = "" if HAVE_FREECAD: - pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons") - noProxyCheck = pref.GetBool("NoProxyCheck", noProxyCheck) - systemProxyCheck = pref.GetBool("SystemProxyCheck", systemProxyCheck) - userProxyCheck = pref.GetBool("UserProxyCheck", userProxyCheck) - proxy_string = pref.GetString("ProxyUrl", "") - - # Add some error checking to the proxy setup, since for historical reasons they - # are independent booleans, rather than an enumeration: - count = [noProxyCheck, systemProxyCheck, userProxyCheck].count(True) - if count != 1: - FreeCAD.Console.PrintWarning( - translate( - "AddonsInstaller", - "Parameter error: mutually exclusive proxy options set. Resetting to default.", - ) - + "\n" - ) - noProxyCheck = True - systemProxyCheck = False - userProxyCheck = False - pref.SetBool("NoProxyCheck", noProxyCheck) - pref.SetBool("SystemProxyCheck", systemProxyCheck) - pref.SetBool("UserProxyCheck", userProxyCheck) - - if userProxyCheck and not proxy_string: - FreeCAD.Console.PrintWarning( - translate( - "AddonsInstaller", - "Parameter error: user proxy indicated, but no proxy provided. Resetting to default.", - ) - + "\n" - ) - noProxyCheck = True - userProxyCheck = False - pref.SetBool("NoProxyCheck", noProxyCheck) - pref.SetBool("UserProxyCheck", userProxyCheck) - + noProxyCheck, systemProxyCheck, userProxyCheck, proxy_string = self._setup_proxy_freecad() else: - print("Please select a proxy type:") - print("1) No proxy") - print("2) Use system proxy settings") - print("3) Custom proxy settings") - result = input("Choice: ") - if result == "1": - pass - elif result == "2": - noProxyCheck = False - systemProxyCheck = True - elif result == "3": - noProxyCheck = False - userProxyCheck = True - proxy_string = input("Enter your proxy server (host:port): ") - else: - print(f"Got {result}, expected 1, 2, or 3.") - app.quit() + noProxyCheck, systemProxyCheck, userProxyCheck, proxy_string = self._setup_proxy_standalone() if noProxyCheck: pass @@ -247,16 +207,80 @@ if HAVE_QTNETWORK: ) self.QNAM.setProxy(proxy) - # A helper connection for our blocking interface - self.completed.connect(self.__synchronous_process_completion) + def _setup_proxy_freecad(self): + """ If we are running within FreeCAD, this uses the config data to set up the proxy """ + noProxyCheck = True + systemProxyCheck = False + userProxyCheck = False + proxy_string = "" + pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Addons") + noProxyCheck = pref.GetBool("NoProxyCheck", noProxyCheck) + systemProxyCheck = pref.GetBool("SystemProxyCheck", systemProxyCheck) + userProxyCheck = pref.GetBool("UserProxyCheck", userProxyCheck) + proxy_string = pref.GetString("ProxyUrl", "") - # Set up our worker connection - self.__request_queued.connect(self.__setup_network_request) + # Add some error checking to the proxy setup, since for historical reasons they + # are independent booleans, rather than an enumeration: + option_count = [noProxyCheck, systemProxyCheck, userProxyCheck].count(True) + if option_count != 1: + FreeCAD.Console.PrintWarning( + translate( + "AddonsInstaller", + "Parameter error: mutually exclusive proxy options set. Resetting to default.", + ) + + "\n" + ) + noProxyCheck = True + systemProxyCheck = False + userProxyCheck = False + pref.SetBool("NoProxyCheck", noProxyCheck) + pref.SetBool("SystemProxyCheck", systemProxyCheck) + pref.SetBool("UserProxyCheck", userProxyCheck) + + if userProxyCheck and not proxy_string: + FreeCAD.Console.PrintWarning( + translate( + "AddonsInstaller", + "Parameter error: user proxy indicated, but no proxy provided. Resetting to default.", + ) + + "\n" + ) + noProxyCheck = True + userProxyCheck = False + pref.SetBool("NoProxyCheck", noProxyCheck) + pref.SetBool("UserProxyCheck", userProxyCheck) + return noProxyCheck, systemProxyCheck, userProxyCheck, proxy_string + + def _setup_proxy_standalone(self): + """ If we are NOT running inside FreeCAD, prompt the user for proxy information """ + noProxyCheck = True + systemProxyCheck = False + userProxyCheck = False + proxy_string = "" + print("Please select a proxy type:") + print("1) No proxy") + print("2) Use system proxy settings") + print("3) Custom proxy settings") + result = input("Choice: ") + if result == "1": + pass + elif result == "2": + noProxyCheck = False + systemProxyCheck = True + elif result == "3": + noProxyCheck = False + userProxyCheck = True + proxy_string = input("Enter your proxy server (host:port): ") + else: + print(f"Got {result}, expected 1, 2, or 3.") + app.quit() + return noProxyCheck, systemProxyCheck, userProxyCheck, proxy_string def __aboutToQuit(self): - pass + """Called when the application is about to quit. Not currently used.""" def __setup_network_request(self): + """Get the next request off the queue and launch it.""" try: item = self.queue.get_nowait() if item: @@ -272,6 +296,7 @@ if HAVE_QTNETWORK: def __launch_request( self, index: int, request: QtNetwork.QNetworkRequest ) -> None: + """Given a network request, ask the QNetworkAccessManager to begin processing it.""" reply = self.QNAM.get(request) self.replies[index] = reply @@ -342,12 +367,12 @@ if HAVE_QTNETWORK: self.synchronous_complete.pop(current_index) if current_index in self.synchronous_result_data: return self.synchronous_result_data.pop(current_index) - else: - return None + return None def __synchronous_process_completion( self, index: int, code: int, data: QtCore.QByteArray ) -> None: + """Check the return status of a completed process, and handle its returned data (if any).""" with self.synchronous_lock: if index in self.synchronous_complete: if code == 200: @@ -363,6 +388,7 @@ if HAVE_QTNETWORK: self.synchronous_complete[index] = True def __create_get_request(self, url: str) -> QtNetwork.QNetworkRequest: + """Construct a network request to a given URL""" request = QtNetwork.QNetworkRequest(QtCore.QUrl(url)) request.setAttribute( QtNetwork.QNetworkRequest.RedirectPolicyAttribute, @@ -390,6 +416,7 @@ if HAVE_QTNETWORK: break def abort(self, index: int): + """Abort a specific request""" if index in self.replies and self.replies[index].isRunning(): self.replies[index].abort() elif index < self.__last_started_index: @@ -401,6 +428,8 @@ if HAVE_QTNETWORK: reply: QtNetwork.QNetworkProxy, authenticator: QtNetwork.QAuthenticator, ): + """If proxy authentication is required, attempt to authenticate. If the GUI is running this displays + a window asking for credentials. If the GUI is not running, it prompts on the command line.""" if HAVE_FREECAD and FreeCAD.GuiUp: proxy_authentication = FreeCADGui.PySideUic.loadUi( os.path.join(os.path.dirname(__file__), "proxy_authentication.ui") @@ -434,9 +463,10 @@ if HAVE_QTNETWORK: _reply: QtNetwork.QNetworkReply, _authenticator: QtNetwork.QAuthenticator, ): - pass + """Unused.""" def __follow_redirect(self, url): + """Used with the QNetworkAccessManager to follow redirects.""" sender = self.sender() if sender: for index, reply in self.replies.items(): @@ -448,6 +478,7 @@ if HAVE_QTNETWORK: self.__launch_request(current_index, self.__create_get_request(url)) def __on_ssl_error(self, reply: str, errors: List[str]): + """Called when an SSL error occurs: prints the error information.""" if HAVE_FREECAD: FreeCAD.Console.PrintWarning( translate("AddonsInstaller", "Error with encrypted connection") @@ -462,6 +493,7 @@ if HAVE_QTNETWORK: print(error) def __download_progress(self, bytesReceived: int, bytesTotal: int) -> None: + """Monitors download progress and emits a progress_made signal""" sender = self.sender() if not sender: return @@ -471,6 +503,7 @@ if HAVE_QTNETWORK: return def __ready_to_read(self) -> None: + """Called when data is available, this reads that data.""" sender = self.sender() if not sender: return @@ -481,6 +514,7 @@ if HAVE_QTNETWORK: return def __data_incoming(self, index: int, reply: QtNetwork.QNetworkReply) -> None: + """Read incoming data and attach it to a data object""" if not index in self.replies: # We already finished this reply, this is a vestigial signal return @@ -492,7 +526,7 @@ if HAVE_QTNETWORK: f = self.file_buffers[index] try: f.write(buffer.data()) - except Exception as e: + except IOError as e: if HAVE_FREECAD: FreeCAD.Console.PrintError( f"Network Manager internal error: {str(e)}" @@ -501,6 +535,8 @@ if HAVE_QTNETWORK: print(f"Network Manager internal error: {str(e)}") def __reply_finished(self) -> None: + """Called when a reply has been completed: this makes sure the data has been read and + any notifications have been called.""" reply = self.sender() if not reply: print( @@ -567,28 +603,32 @@ else: # HAVE_QTNETWORK is false: self.unmonitored_queue = queue.Queue() def submit_unmonitored_request(self, _) -> int: + """Returns a fake index that can be used for testing -- nothing is actually queued""" current_index = next(itertools.count()) self.unmonitored_queue.put(current_index) return current_index def submit_monitored_request(self, _) -> int: + """Returns a fake index that can be used for testing -- nothing is actually queued""" current_index = next(itertools.count()) self.monitored_queue.put(current_index) return current_index def blocking_get(self, _: str) -> QtCore.QByteArray: + """No operation - returns None immediately""" return None def abort_all( self, ): - pass # Nothing to do + """There is nothing to abort in this case""" def abort(self, _): - pass # Nothing to do + """There is nothing to abort in this case""" def InitializeNetworkManager(): + """Called once at the beginning of program execution to create the appropriate manager object""" global AM_NETWORK_MANAGER if AM_NETWORK_MANAGER is None: AM_NETWORK_MANAGER = NetworkManager() @@ -611,6 +651,7 @@ if __name__ == "__main__": ] def handle_completion(index: int, code: int, data): + """Attached to the completion signal, prints diagnostic information about the network access""" global count if code == 200: print( @@ -624,14 +665,14 @@ if __name__ == "__main__": count += 1 if count >= len(urls): - print(f"Shutting down...", flush=True) + print("Shutting down...", flush=True) AM_NETWORK_MANAGER.requestInterruption() AM_NETWORK_MANAGER.wait(5000) app.quit() AM_NETWORK_MANAGER.completed.connect(handle_completion) - for url in urls: - AM_NETWORK_MANAGER.submit_unmonitored_get(url) + for test_url in urls: + AM_NETWORK_MANAGER.submit_unmonitored_get(test_url) app.exec_()