def _startListenNotifications(self): if not Controller.instance().connected(): return # Qt websocket before Qt 5.6 doesn't support auth if parse_version(QtCore.QT_VERSION_STR) < parse_version( "5.6.0") or parse_version( QtCore.PYQT_VERSION_STR) < parse_version("5.6.0"): path = "/projects/{project_id}/notifications".format( project_id=self._id) self._notification_stream = Controller.instance().createHTTPQuery( "GET", path, self._endListenNotificationCallback, downloadProgressCallback=self._event_received, networkManager=self._notification_network_manager, timeout=None, showProgress=False, ignoreErrors=True) else: path = "/projects/{project_id}/notifications/ws".format( project_id=self._id) self._notification_stream = Controller.instance().connectWebSocket( path) self._notification_stream.textMessageReceived.connect( self._websocket_event_received) self._notification_stream.error.connect(self._websocket_error)
def _pypiReplySlot(self): network_reply = self.sender() if network_reply.error() != QtNetwork.QNetworkReply.NoError: if not self._silent: QtWidgets.QMessageBox.critical(self._parent, "Check For Update", "Cannot check for update: {}".format(network_reply.errorString())) return try: body = bytes(network_reply.readAll()).decode("utf-8") body = json.loads(body) except (UnicodeEncodeError, ValueError) as e: log.warning("Invalid answer from the PyPi server") last_version = self._getLastMinorVersionFromPyPiReply(body) if parse_version(last_version) > parse_version(version.__version__): reply = QtWidgets.QMessageBox.question(self._parent, "Check For Update", "Newer GNS3 version {} is available, do you want to to download it in background and install it at next application launch?".format(last_version), QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.Yes: try: self.downloadUpdates(last_version) except OSError as e: QtWidgets.QMessageBox.critical(self._parent, "Check For Update", "Cannot download update: {}".format(e)) else: self._get('http://update.gns3.net', self._gns3UpdateReplySlot)
def _gns3UpdateReplySlot(self): network_reply = self.sender() if network_reply.error() != QtNetwork.QNetworkReply.NoError: if not self._silent: QtWidgets.QMessageBox.critical( self._parent, "Check For Update", "Cannot check for update: {}".format( network_reply.errorString())) return try: latest_release = bytes( network_reply.readAll()).decode("utf-8").rstrip() except UnicodeDecodeError: log.warning("Invalid answer from the update server") return if re.match(r"^[a-z0-9\.]+$", latest_release) is None: log.warning("Invalid answer from the update server") return if parse_version(version.__version__) < parse_version(latest_release): reply = QtWidgets.QMessageBox.question( self._parent, "Check For Update", "Newer GNS3 version {} is available, do you want to visit our website to download it?" .format(latest_release), QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.Yes: QtGui.QDesktopServices.openUrl( QtCore.QUrl("http://www.gns3.net/download/")) elif not self._silent: QtWidgets.QMessageBox.information(self._parent, "Check For Update", "GNS3 is up-to-date!")
def __init__(self, argv, hdpi=True): self.setStyle(QtWidgets.QStyleFactory.create("Fusion")) # both Qt and PyQt must be version >= 5.6 in order to enable high DPI scaling if parse_version(QtCore.QT_VERSION_STR) >= parse_version("5.6") and parse_version(QtCore.PYQT_VERSION_STR) >= parse_version("5.6"): # only available starting Qt version 5.6 if hdpi: if sys.platform.startswith("linux"): log.warning("HDPI mode is enabled. HDPI support on Linux is not fully stable and GNS3 may crash depending of your version of Linux. To disabled HDPI mode please edit ~/.config/GNS3/gns3_gui.conf and set 'hdpi' to 'false'") self.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) self.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps) else: log.info("HDPI mode is disabled") self.setAttribute(QtCore.Qt.AA_DisableHighDpiScaling) super().__init__(argv) # this info is necessary for QSettings self.setOrganizationName("GNS3") self.setOrganizationDomain("gns3.net") self.setApplicationName("GNS3") self.setApplicationVersion(__version__) # File path if we have received the path to # a file on system via an OSX event self.open_file_at_startup = None
def _startListenNotifications(self): if not self.connected(): return # Due to bug in Qt on some version we need a dedicated network manager self._notification_network_manager = QtNetwork.QNetworkAccessManager() self._notification_stream = None # Qt websocket before Qt 5.6 doesn't support auth if parse_version(QtCore.QT_VERSION_STR) < parse_version( "5.6.0") or parse_version( QtCore.PYQT_VERSION_STR) < parse_version("5.6.0"): self._notification_stream = Controller.instance().createHTTPQuery( "GET", "/notifications", self._endListenNotificationCallback, downloadProgressCallback=self._event_received, networkManager=self._notification_network_manager, timeout=None, showProgress=False, ignoreErrors=True) else: self._notification_stream = self._http_client.connectWebSocket( self._websocket, "/notifications/ws") self._notification_stream.textMessageReceived.connect( self._websocket_event_received) self._notification_stream.error.connect(self._websocket_error)
def _gns3UpdateReplySlot(self): network_reply = self.sender() if network_reply is None: return if network_reply.error() != QtNetwork.QNetworkReply.NoError: if not self._silent: QtWidgets.QMessageBox.critical(self._parent, "Check For Update", "Cannot check for update: {}".format(network_reply.errorString())) return try: latest_release = bytes(network_reply.readAll()).decode("utf-8").rstrip() except UnicodeDecodeError: log.debug("Invalid answer from the update server") return if re.match(r"^[a-z0-9\.]+$", latest_release) is None: log.debug("Invalid answer from the update server") return if parse_version(version.__version__) < parse_version(latest_release): reply = QtWidgets.QMessageBox.question(self._parent, "Check For Update", "Newer GNS3 version {} is available, do you want to visit our website to download it?".format(latest_release), QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.Yes: QtGui.QDesktopServices.openUrl(QtCore.QUrl("http://www.gns3.com/software")) elif not self._silent: QtWidgets.QMessageBox.information(self._parent, "Check For Update", "GNS3 is up-to-date!")
def _pypiReplySlot(self): network_reply = self.sender() if network_reply.error() != QtNetwork.QNetworkReply.NoError: if not self._silent: QtWidgets.QMessageBox.critical( self._parent, "Check For Update", "Cannot check for update: {}".format( network_reply.errorString())) return try: body = bytes(network_reply.readAll()).decode("utf-8") body = json.loads(body) except (UnicodeEncodeError, ValueError) as e: log.warning("Invalid answer from the PyPi server") last_version = self._getLastMinorVersionFromPyPiReply(body) if parse_version(last_version) > parse_version(version.__version__): reply = QtWidgets.QMessageBox.question( self._parent, "Check For Update", "Newer GNS3 version {} is available, do you want to to download it in background and install it at next application launch?" .format(last_version), QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No) if reply == QtWidgets.QMessageBox.Yes: try: self.downloadUpdates(last_version) except OSError as e: QtWidgets.QMessageBox.critical( self._parent, "Check For Update", "Cannot download update: {}".format(e)) else: self._get('http://update.gns3.net', self._gns3UpdateReplySlot)
def __init__(self, argv, hdpi=True): self.setStyle(QtWidgets.QStyleFactory.create("Fusion")) # both Qt and PyQt must be version >= 5.6 in order to enable high DPI scaling if parse_version(QtCore.QT_VERSION_STR) >= parse_version( "5.6") and parse_version( QtCore.PYQT_VERSION_STR) >= parse_version("5.6"): # only available starting Qt version 5.6 if hdpi: if sys.platform.startswith("linux"): log.warning( "HDPI mode is enabled. HDPI support on Linux is not fully stable and GNS3 may crash depending of your version of Linux. To disabled HDPI mode please edit ~/.config/GNS3/gns3_gui.conf and set 'hdpi' to 'false'" ) self.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) self.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps) else: log.info("HDPI mode is disabled") self.setAttribute(QtCore.Qt.AA_DisableHighDpiScaling) super().__init__(argv) # this info is necessary for QSettings self.setOrganizationName("GNS3") self.setOrganizationDomain("gns3.net") self.setApplicationName("GNS3") self.setApplicationVersion(__version__) # File path if we have received the path to # a file on system via an OSX event self.open_file_at_startup = None
def _check_vpcs_version(self, working_dir): """ Checks if the VPCS executable version is >= 0.5b1. """ try: output = subprocess.check_output([self._settings["vpcs_path"], "-v"], cwd=working_dir) match = re.search("Welcome to Virtual PC Simulator, version ([0-9a-z\.]+)", output.decode("utf-8")) if match: version = match.group(1) if parse_version(version) < parse_version("0.5b1"): raise ModuleError("VPCS executable version must be >= 0.5b1") else: raise ModuleError("Could not determine the VPCS version for {}".format(self._settings["vpcs_path"])) except (OSError, subprocess.SubprocessError) as e: raise ModuleError("Error while looking for the VPCS version: {}".format(e))
def _getLastMinorVersionFromPyPiReply(self, body): """ Return the most recent minor version for this release from a PyPi answer. If no valid version is found it's return the current. """ current_version = parse_version(version.__version__) for release in sorted(body['releases'].keys(), reverse=True): release_version = parse_version(release) if release_version[-1:][0] == "final": if self.isDevVersion(): continue else: if not self.isDevVersion(): continue if release_version > current_version and release_version[:2] == current_version[:2]: return release return version.__version__
def _startListenNotifications(self): if not Controller.instance().connected(): return # Qt websocket before Qt 5.6 doesn't support auth if parse_version(QtCore.QT_VERSION_STR) < parse_version("5.6.0") or parse_version(QtCore.PYQT_VERSION_STR) < parse_version("5.6.0"): path = "/projects/{project_id}/notifications".format(project_id=self._id) self._notification_stream = Controller.instance().createHTTPQuery("GET", path, self._endListenNotificationCallback, downloadProgressCallback=self._event_received, networkManager=self._notification_network_manager, timeout=None, showProgress=False, ignoreErrors=True) else: path = "/projects/{project_id}/notifications/ws".format(project_id=self._id) self._notification_stream = Controller.instance().connectWebSocket(path) self._notification_stream.textMessageReceived.connect(self._websocket_event_received) self._notification_stream.error.connect(self._websocket_error)
def _get(self, url, finished_slot, user_attribute=None): """ HTTP get :param url: Url to download :param user_attribute: Param to pass to the finished slot :returns: QNetworkReply """ if self._network_manager is None: self._network_manager = QtNetwork.QNetworkAccessManager() request = QtNetwork.QNetworkRequest(QtCore.QUrl(url)) request.setRawHeader(b'User-Agent', b'GNS3 Check For Update') request.setAttribute(QtNetwork.QNetworkRequest.User, user_attribute) if parse_version(QtCore.QT_VERSION_STR) >= parse_version("5.6.0") and parse_version(QtCore.PYQT_VERSION_STR) >= parse_version("5.6.0"): # follow redirects only supported starting with Qt 5.6.0 request.setAttribute(QtNetwork.QNetworkRequest.FollowRedirectsAttribute, True) reply = self._network_manager.get(request) reply.finished.connect(finished_slot) log.debug('Download %s', url) return reply
def __init__(self, argv): self.setStyle(QtWidgets.QStyleFactory.create("Fusion")) # both Qt and PyQt must be version >= 5.6 in order to enable high DPI scaling if parse_version(QtCore.QT_VERSION_STR) >= parse_version("5.6") and parse_version(QtCore.PYQT_VERSION_STR) >= parse_version("5.6"): # only available starting Qt version 5.6 self.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) self.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps) super().__init__(argv) # this info is necessary for QSettings self.setOrganizationName("GNS3") self.setOrganizationDomain("gns3.net") self.setApplicationName("GNS3") self.setApplicationVersion(__version__) # File path if we have received the path to # a file on system via an OSX event self.open_file_at_startup = None
def _startListenNotifications(self): if not self.connected(): return # Due to bug in Qt on some version we need a dedicated network manager self._notification_network_manager = QtNetwork.QNetworkAccessManager() self._notification_stream = None # Qt websocket before Qt 5.6 doesn't support auth if parse_version(QtCore.QT_VERSION_STR) < parse_version("5.6.0") or parse_version(QtCore.PYQT_VERSION_STR) < parse_version("5.6.0"): self._notification_stream = Controller.instance().createHTTPQuery("GET", "/notifications", self._endListenNotificationCallback, downloadProgressCallback=self._event_received, networkManager=self._notification_network_manager, timeout=None, showProgress=False, ignoreErrors=True) else: self._notification_stream = self._http_client.connectWebSocket(self._controller_websocket, "/notifications/ws") self._notification_stream.textMessageReceived.connect(self._websocket_event_received) self._notification_stream.error.connect(self._websocket_error)
def __init__(self, argv): self.setStyle(QtWidgets.QStyleFactory.create("Fusion")) # both Qt and PyQt must be version >= 5.6 in order to enable high DPI scaling if parse_version(QtCore.QT_VERSION_STR) >= parse_version( "5.6") and parse_version( QtCore.PYQT_VERSION_STR) >= parse_version("5.6"): # only available starting Qt version 5.6 self.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling) self.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps) super().__init__(argv) # this info is necessary for QSettings self.setOrganizationName("GNS3") self.setOrganizationDomain("gns3.net") self.setApplicationName("GNS3") self.setApplicationVersion(__version__) # File path if we have received the path to # a file on system via an OSX event self.open_file_at_startup = None
def main(): """ Entry point for GNS3 GUI. """ # Sometimes (for example at first launch) the OSX app service launcher add # an extra argument starting with -psn_. We filter it if sys.platform.startswith("darwin"): sys.argv = [a for a in sys.argv if not a.startswith("-psn_")] parser = argparse.ArgumentParser() parser.add_argument("project", help="load a GNS3 project (.gns3)", metavar="path", nargs="?") parser.add_argument("--version", help="show the version", action="version", version=__version__) parser.add_argument("--debug", help="print out debug messages", action="store_true", default=False) parser.add_argument("--config", help="Configuration file") options = parser.parse_args() exception_file_path = "exceptions.log" if options.config: LocalConfig.instance(config_file=options.config) else: LocalConfig.instance() if options.project: options.project = os.path.abspath(options.project) if hasattr(sys, "frozen"): # We add to the path where the OS search executable our binary location starting by GNS3 # packaged binary frozen_dir = os.path.dirname(os.path.abspath(sys.executable)) if sys.platform.startswith("darwin"): frozen_dirs = [ frozen_dir, os.path.normpath(os.path.join(frozen_dir, '..', 'Resources')) ] elif sys.platform.startswith("win"): frozen_dirs = [ frozen_dir, os.path.normpath(os.path.join(frozen_dir, 'dynamips')), os.path.normpath(os.path.join(frozen_dir, 'vpcs')) ] os.environ["PATH"] = os.pathsep.join(frozen_dirs) + os.pathsep + os.environ.get("PATH", "") if options.project: os.chdir(frozen_dir) def exceptionHook(exception, value, tb): if exception == KeyboardInterrupt: sys.exit(0) lines = traceback.format_exception(exception, value, tb) print("****** Exception detected, traceback information saved in {} ******".format(exception_file_path)) print("\nPLEASE REPORT ON https://www.gns3.com\n") print("".join(lines)) try: curdate = time.strftime("%d %b %Y %H:%M:%S") logfile = open(exception_file_path, "a", encoding="utf-8") logfile.write("=== GNS3 {} traceback on {} ===\n".format(__version__, curdate)) logfile.write("".join(lines)) logfile.close() except OSError as e: print("Could not save traceback to {}: {}".format(os.path.normpath(exception_file_path), e)) if not sys.stdout.isatty(): # if stdout is not a tty (redirected to the console view), # then print the exception on stderr too. print("".join(lines), file=sys.stderr) if exception is MemoryError: print("YOUR SYSTEM IS OUT OF MEMORY!") else: CrashReport.instance().captureException(exception, value, tb) # catch exceptions to write them in a file sys.excepthook = exceptionHook current_year = datetime.date.today().year print("GNS3 GUI version {}".format(__version__)) print("Copyright (c) 2007-{} GNS3 Technologies Inc.".format(current_year)) # we only support Python 3 version >= 3.4 if sys.version_info < (3, 4): raise SystemExit("Python 3.4 or higher is required") if parse_version(QtCore.QT_VERSION_STR) < parse_version("5.0.0"): raise SystemExit("Requirement is PyQt5 version 5.0.0 or higher, got version {}".format(QtCore.QT_VERSION_STR)) if parse_version(psutil.__version__) < parse_version("2.2.1"): raise SystemExit("Requirement is psutil version 2.2.1 or higher, got version {}".format(psutil.__version__)) # check for the correct locale # (UNIX/Linux only) locale_check() try: os.getcwd() except FileNotFoundError: log.critical("the current working directory doesn't exist") return # always use the INI format on Windows and OSX (because we don't like the registry and plist files) if sys.platform.startswith('win') or sys.platform.startswith('darwin'): QtCore.QSettings.setDefaultFormat(QtCore.QSettings.IniFormat) if sys.platform.startswith('win') and hasattr(sys, "frozen"): try: import win32console import win32con import win32gui except ImportError: raise SystemExit("Python for Windows extensions must be installed.") if not options.debug: try: # hide the console console_window = win32console.GetConsoleWindow() win32gui.ShowWindow(console_window, win32con.SW_HIDE) except win32console.error as e: print("warning: could not allocate console: {}".format(e)) global app app = Application(sys.argv) # save client logging info to a file logfile = os.path.join(LocalConfig.configDirectory(), "gns3_gui.log") # on debug enable logging to stdout if options.debug: root_logger = init_logger(logging.DEBUG, logfile) else: root_logger = init_logger(logging.INFO, logfile) # update the exception file path to have it in the same directory as the settings file. exception_file_path = os.path.join(LocalConfig.configDirectory(), exception_file_path) global mainwindow mainwindow = MainWindow() # On OSX we can receive the file to open from a system event # loadPath is smart and will load only if a path is present mainwindow.ready_signal.connect(lambda: mainwindow.loadPath(app.open_file_at_startup)) mainwindow.ready_signal.connect(lambda: mainwindow.loadPath(options.project)) app.file_open_signal.connect(lambda path: mainwindow.loadPath(path)) # Manage Ctrl + C or kill command def sigint_handler(*args): log.info("Signal received exiting the application") mainwindow.setSoftExit(False) app.closeAllWindows() orig_sigint = signal.signal(signal.SIGINT, sigint_handler) orig_sigterm = signal.signal(signal.SIGTERM, sigint_handler) mainwindow.show() exit_code = app.exec_() signal.signal(signal.SIGINT, orig_sigint) signal.signal(signal.SIGTERM, orig_sigterm) delattr(MainWindow, "_instance") # We force deleting the app object otherwise it's segfault on Fedora del app # We force a full garbage collect before exit # for unknow reason otherwise Qt Segfault on OSX in some # conditions import gc gc.collect() sys.exit(exit_code)
def test_parse_version(): assert parse_version('1') == ('000001', '00000', '000000', 'final') assert parse_version('1.3') == ('000001', '000003', '000000', 'final') assert parse_version('1.3.dev3') == ('000001', '000003', '000000', 'dev', '000003') assert parse_version('1.3a1') == ('000001', '000003', '000000', 'a', '000001') assert parse_version('1.3rc1') == ('000001', '000003', '000000', 'c', '000001') assert parse_version('1.2.3') > parse_version('1.2.2') assert parse_version('1.3') > parse_version('1.2.2') assert parse_version('1.3') > parse_version('1.3alpha1') assert parse_version('1.3') > parse_version('1.3rc1') assert parse_version('1.3rc1') > parse_version('1.3alpha3') assert parse_version('1.3dev1') > parse_version('1.3rc1') assert parse_version('1.2.3') > parse_version('1.2')
def main(): """ Entry point for GNS3 GUI. """ # Get Python tracebacks explicitly, on a fault like segfault faulthandler.enable() # Sometimes (for example at first launch) the OSX app service launcher add # an extra argument starting with -psn_. We filter it if sys.platform.startswith("darwin"): sys.argv = [a for a in sys.argv if not a.startswith("-psn_")] if parse_version(QtCore.QT_VERSION_STR) < parse_version("5.15.2"): # Fixes issue on macOS Big Sur: https://github.com/GNS3/gns3-gui/issues/3037 os.environ["QT_MAC_WANTS_LAYER"] = "1" parser = argparse.ArgumentParser() parser.add_argument("project", help="load a GNS3 project (.gns3)", metavar="path", nargs="?") parser.add_argument("--version", help="show the version", action="version", version=__version__) parser.add_argument("--debug", help="print out debug messages", action="store_true", default=False) parser.add_argument("-q", "--quiet", action="store_true", help="do not show logs on stdout") parser.add_argument("--config", help="Configuration file") parser.add_argument("--profile", help="Settings profile (blank will use default settings files)") options = parser.parse_args() exception_file_path = "exceptions.log" if options.project: options.project = os.path.abspath(options.project) if hasattr(sys, "frozen"): # We add to the path where the OS search executable our binary location starting by GNS3 # packaged binary frozen_dir = os.path.dirname(os.path.abspath(sys.executable)) if sys.platform.startswith("darwin"): frozen_dirs = [frozen_dir] elif sys.platform.startswith("win"): frozen_dirs = [ frozen_dir, os.path.normpath(os.path.join(frozen_dir, 'dynamips')), os.path.normpath(os.path.join(frozen_dir, 'vpcs')), os.path.normpath(os.path.join(frozen_dir, 'traceng')) ] os.environ["PATH"] = os.pathsep.join(frozen_dirs) + os.pathsep + os.environ.get("PATH", "") if options.project: os.chdir(frozen_dir) def exceptionHook(exception, value, tb): if exception == KeyboardInterrupt: sys.exit(0) lines = traceback.format_exception(exception, value, tb) print("****** Exception detected, traceback information saved in {} ******".format(exception_file_path)) print("\nPLEASE REPORT ON https://www.gns3.com\n") print("".join(lines)) try: curdate = time.strftime("%d %b %Y %H:%M:%S") logfile = open(exception_file_path, "a", encoding="utf-8") logfile.write("=== GNS3 {} traceback on {} ===\n".format(__version__, curdate)) logfile.write("".join(lines)) logfile.close() except OSError as e: print("Could not save traceback to {}: {}".format(os.path.normpath(exception_file_path), e)) if not sys.stdout.isatty(): # if stdout is not a tty (redirected to the console view), # then print the exception on stderr too. print("".join(lines), file=sys.stderr) if exception is MemoryError: print("YOUR SYSTEM IS OUT OF MEMORY!") else: CrashReport.instance().captureException(exception, value, tb) # catch exceptions to write them in a file sys.excepthook = exceptionHook # we only support Python 3 version >= 3.4 if sys.version_info < (3, 4): raise SystemExit("Python 3.4 or higher is required") if parse_version(QtCore.QT_VERSION_STR) < parse_version("5.5.0"): raise SystemExit("Requirement is PyQt5 version 5.5.0 or higher, got version {}".format(QtCore.QT_VERSION_STR)) if parse_version(psutil.__version__) < parse_version("2.2.1"): raise SystemExit("Requirement is psutil version 2.2.1 or higher, got version {}".format(psutil.__version__)) # check for the correct locale # (UNIX/Linux only) locale_check() try: os.getcwd() except FileNotFoundError: log.critical("the current working directory doesn't exist") return # always use the INI format on Windows and OSX (because we don't like the registry and plist files) if sys.platform.startswith('win') or sys.platform.startswith('darwin'): QtCore.QSettings.setDefaultFormat(QtCore.QSettings.IniFormat) if sys.platform.startswith('win') and hasattr(sys, "frozen"): try: import win32console import win32con import win32gui except ImportError: raise SystemExit("Python for Windows extensions must be installed.") if not options.debug: try: # hide the console console_window = win32console.GetConsoleWindow() win32gui.ShowWindow(console_window, win32con.SW_HIDE) except win32console.error as e: print("warning: could not allocate console: {}".format(e)) local_config = LocalConfig.instance() global app app = Application(sys.argv, hdpi=local_config.hdpi()) if local_config.multiProfiles() and not options.profile: profile_select = ProfileSelectDialog() profile_select.show() if profile_select.exec_(): options.profile = profile_select.profile() else: sys.exit(0) # Init the config if options.config: local_config.setConfigFilePath(options.config) elif options.profile: local_config.setProfile(options.profile) # save client logging info to a file logfile = os.path.join(LocalConfig.instance().configDirectory(), "gns3_gui.log") # on debug enable logging to stdout if options.debug: init_logger(logging.DEBUG, logfile) elif options.quiet: init_logger(logging.ERROR, logfile) else: init_logger(logging.INFO, logfile) current_year = datetime.date.today().year log.info("GNS3 GUI version {}".format(__version__)) log.info("Copyright (c) 2007-{} GNS3 Technologies Inc.".format(current_year)) log.info("Application started with {}".format(" ".join(sys.argv))) # update the exception file path to have it in the same directory as the settings file. exception_file_path = os.path.join(LocalConfig.instance().configDirectory(), exception_file_path) # We disallow to run GNS3 from outside the /Applications folder to avoid # issue when people run GNS3 from the .dmg if sys.platform.startswith("darwin") and hasattr(sys, "frozen"): if not os.path.realpath(sys.executable).startswith("/Applications"): error_message = "GNS3.app must be moved to the '/Applications' folder before it can be used" QtWidgets.QMessageBox.critical(False, "Loading error", error_message) QtCore.QTimer.singleShot(0, app.quit) app.exec_() sys.exit(1) global mainwindow startup_file = app.open_file_at_startup if not startup_file: startup_file = options.project mainwindow = MainWindow(open_file=startup_file) # On OSX we can receive the file to open from a system event # loadPath is smart and will load only if a path is present app.file_open_signal.connect(lambda path: mainwindow.loadPath(path)) # Manage Ctrl + C or kill command def sigint_handler(*args): log.info("Signal received exiting the application") app.closeAllWindows() orig_sigint = signal.signal(signal.SIGINT, sigint_handler) orig_sigterm = signal.signal(signal.SIGTERM, sigint_handler) mainwindow.show() exit_code = app.exec_() signal.signal(signal.SIGINT, orig_sigint) signal.signal(signal.SIGTERM, orig_sigterm) delattr(MainWindow, "_instance") # We force deleting the app object otherwise it's segfault on Fedora del app # We force a full garbage collect before exit # for unknown reason otherwise Qt Segfault on OSX in some # conditions import gc gc.collect() sys.exit(exit_code)
def test_parse_version(): assert parse_version('1') == (1, 'final') assert parse_version('1.3') == (1, 3, 'final') assert parse_version('1.3.dev3') == (1, 3, 'dev', 3) assert parse_version('1.3a1') == (1, 3, 'a', 1) assert parse_version('1.3rc1') == (1, 3, 'c', 1) assert parse_version('1.2.3') > parse_version('1.2.2') assert parse_version('1.3') > parse_version('1.2.2') assert parse_version('1.3') > parse_version('1.3alpha1') assert parse_version('1.3') > parse_version('1.3rc1') assert parse_version('1.3rc1') > parse_version('1.3alpha3') assert parse_version('1.3dev1') > parse_version('1.3rc1')