def __init_websockets(splash, app_bootstrap, settings): """ Initializes the local websocket server. :pram splash: Splash widget. :param app_bootstrap: The application bootstrap instance. :param settings: The application's settings. :returns: The tk_framework_desktopserver.Server instance and a boolean indicating if the Desktop should keep launching. """ if not __is_64bit_python(): # Do not import if Python is not 64-bits logger.warning("Interpreter is not 64-bits, can't load desktop server") return None, True if not settings.integration_enabled: # Do not import if server is disabled. logger.info("Integration was disabled in config.ini.") return None, True # First try to import the framework. If it fails, let the user decide if the Desktop should # keep launching. try: splash.show() splash.set_message("Initializing browser integration") # Import framework import tk_framework_desktopserver app_bootstrap.add_logger_to_logfile( tk_framework_desktopserver.get_logger()) except Exception, e: return None, __handle_unexpected_exception_during_websocket_init( splash, app_bootstrap, e)
def _assert_toolkit_enabled(splash, connection): """ Returns the path to the pipeline configuration for a given site. :param splash: Splash dialog """ # get the pipeline configuration for the site we are logged into while True: pc_schema = connection.schema_entity_read().get( "PipelineConfiguration") if pc_schema is not None: break # Toolkit is not turned on show the dialog that explains what to do splash.hide() dialog = TurnOnToolkit(connection) dialog.show() dialog.raise_() dialog.activateWindow() results = dialog.exec_() if results == dialog.Rejected: # dialog was canceled, raise the exception and let the main exception handler deal # with it. raise ToolkitDisabledError() splash.show()
def _assert_toolkit_enabled(splash, connection): """ Returns the path to the pipeline configuration for a given site. :param splash: Splash dialog """ # get the pipeline configuration for the site we are logged into while True: pc_schema = connection.schema_entity_read().get("PipelineConfiguration") if pc_schema is not None: break # Toolkit is not turned on show the dialog that explains what to do splash.hide() dialog = TurnOnToolkit(connection) dialog.show() dialog.raise_() dialog.activateWindow() results = dialog.exec_() if results == dialog.Rejected: # dialog was canceled, raise the exception and let the main exception handler deal # with it. raise ToolkitDisabledError() splash.show()
def __init_websockets(splash, app_bootstrap, settings): """ Initializes the local websocket server. :pram splash: Splash widget. :param app_bootstrap: The application bootstrap instance. :param settings: The application's settings. :returns: The tk_framework_desktopserver.Server instance and a boolean indicating if the Desktop should keep launching. """ if not __is_64bit_python(): # Do not import if Python is not 64-bits logger.warning("Interpreter is not 64-bits, can't load desktop server") return None, True if not settings.integration_enabled: # Do not import if server is disabled. logger.info("Integration was disabled in config.ini.") return None, True # First try to import the framework. If it fails, let the user decide if the Desktop should # keep launching. try: splash.show() splash.set_message("Initializing browser integration") # Import framework import tk_framework_desktopserver app_bootstrap.add_logger_to_logfile(tk_framework_desktopserver.get_logger()) except Exception, e: return None, __handle_unexpected_exception_during_websocket_init(splash, app_bootstrap, e)
def __launch_app(app, splash, user, app_bootstrap, settings): """ Shows the splash screen, optionally downloads and configures Toolkit, imports it, optionally updates it and then launches the desktop engine. :param app: Application object for event processing. :param splash: Splash dialog to update user on what is currently going on :param user: Current ShotgunUser. :param app_bootstrap: Application bootstrap. :param settings: The application's settings. :returns: The error code to return to the shell. """ # show the splash screen splash.show() import sgtk sgtk.set_authenticated_user(user) # Downloads an upgrade for the startup if available. startup_updated = upgrade_startup( splash, sgtk, app_bootstrap ) if startup_updated: __restart_app_with_countdown(splash, "Shotgun Desktop updated.") splash.set_message("Looking up site configuration.") connection = user.create_sg_connection() _assert_toolkit_enabled(splash, connection) logger.debug("Getting the default site configuration.") pc_path, pc, toolkit_classic_required = shotgun_desktop.paths.get_pipeline_configuration_info(connection) # We're about to bootstrap, so remove sgtk from our scope so that if we add # code that uses it after the bootstrap we have to import the # new core. del sgtk if toolkit_classic_required: engine = __start_engine_in_toolkit_classic(app, splash, user, pc, pc_path) else: engine = __start_engine_in_zero_config(app, app_bootstrap, splash, user) return __post_bootstrap_engine(splash, app_bootstrap, engine, settings)
def __restart_app_with_countdown(splash, reason): """ Restarts the app after displaying a countdown. :param splash: Splash dialog, used to display the countdown. :param reason: Reason to display in the dialog for the restart. :throws RequestRestartException: This method never returns and throws """ # Provide a countdown so the user knows that the desktop app is being restarted # on purpose because of a core update. Otherwise, the user would get a flickering # splash screen that from the user point of view looks like the app is redoing work # it already did by mistake. This makes the behavior explicit. splash.show() splash.raise_() splash.activateWindow() for i in range(3, 0, -1): splash.set_message("%s Restarting in %d seconds..." % (reason, i)) time.sleep(1) raise RequestRestartException()
def __launch_app(app, splash, user, app_bootstrap, settings): """ Shows the splash screen, optionally downloads and configures Toolkit, imports it, optionally updates it and then launches the desktop engine. :param app: Application object for event processing. :param splash: Splash dialog to update user on what is currently going on :param user: Current ShotgunUser. :param app_bootstrap: Application bootstrap. :param settings: The application's settings. :returns: The error code to return to the shell. """ # show the splash screen splash.show() import sgtk sgtk.set_authenticated_user(user) # Downloads an upgrade for the startup if available. startup_updated = upgrade_startup(splash, sgtk, app_bootstrap) if startup_updated: # We need to restore the global debug logging setting prior to # restarting the app so that forced debug logging is not remembered # as the startup state. Once Desktop relaunches it will resume forced # debug logging until core swap, when the original launch setting is # restored. __restore_global_debug_flag() __restart_app_with_countdown(splash, "Shotgun Desktop updated.") splash.set_message("Looking up site configuration.") connection = user.create_sg_connection() _assert_toolkit_enabled(splash, connection) logger.debug("Getting the default site configuration.") pc_path, pc, toolkit_classic_required = shotgun_desktop.paths.get_pipeline_configuration_info( connection) # We need to toggle the global debug logging setting back prior to swapping # core. Cores older than v0.18.117 do not manage the TK_DEBUG environment # variable when global debug is toggled, so we can end up in a situation # where the new core in desktopstartup toggles it on, setting the env var, # and then when the toggle off occurs we end up with the older core in the # site config NOT purging the TK_DEBUG env var. The result is that any # subprocesses spawned then end up with debug logging on when the user # didn't ask for it. This impacts engine command execution from both Desktop # and via browser integration. It is the biggest issue with browser # integration, however, as the debug logs are reported back to the web # app from the desktopserver RPC API, and the user is presented with a # dialog full of debug log messages. __restore_global_debug_flag() # We're about to bootstrap, so remove sgtk from our scope so that if we add # code that uses it after the bootstrap we have to import the # new core. del sgtk if toolkit_classic_required: engine = __start_engine_in_toolkit_classic(app, splash, user, pc, pc_path) else: engine = __start_engine_in_zero_config(app, app_bootstrap, splash, user) return __post_bootstrap_engine(splash, app_bootstrap, engine, settings)
def __launch_app(app, splash, connection, app_bootstrap, server, settings): """ Shows the splash screen, optionally downloads and configures Toolkit, imports it, optionally updates it and then launches the desktop engine. :param app: Application object for event processing. :param splash: Splash dialog to update user on what is currently going on :param connection: Connection to the Shotgun server. :param server: The tk_framework_desktopserver.Server instance. :param settings: The application's settings. :returns: The error code to return to the shell. """ # show the splash screen splash.show() splash.set_message("Looking up site configuration.") app.processEvents() _assert_toolkit_enabled(splash, connection) logger.debug("Getting the default site config") default_site_config, pc = shotgun_desktop.paths.get_default_site_config_root( connection) # try and import toolkit toolkit_imported = False config_folder_exists_at_startup = os.path.exists(default_site_config) reset_site = __extract_command_line_argument("--reset-site") # If the config folder exists at startup but the user wants to wipe it, do it. if config_folder_exists_at_startup and reset_site: logger.info("Resetting site configuration at '%s'" % default_site_config) splash.set_message("Resetting site configuration ...") shutil.rmtree(default_site_config) # It doesn't exist anymore, so we can act as if it never existed in the first place config_folder_exists_at_startup = False # Remove all occurances of --reset-site so that if we restart the app it doesn't reset it # again. # If there is no pipeline configuration but we found something on disk nonetheless. if not pc and is_toolkit_already_configured(default_site_config): raise UnexpectedConfigFound(default_site_config) try: # In we found a pipeline configuration and the path for the config exists, try to import # Toolkit. if config_folder_exists_at_startup: logger.info("Trying site config from '%s'" % default_site_config) sgtk = __import_sgtk_from_path(default_site_config) toolkit_imported = True except Exception: logger.exception("There was an error importing Toolkit:") pass else: # Toolkit was imported, we need to initialize it now. if toolkit_imported: __initialize_sgtk_authentication(sgtk, app_bootstrap) if not toolkit_imported: # sgtk not available. initialize core logger.info("Import sgtk from site config failed. ") try: app.processEvents() splash.set_message("Initializing Toolkit") logger.info("Initializing Toolkit") core_path = initialize(splash, connection, settings.default_app_store_http_proxy) except Exception, error: logger.exception(error) if "ApiUser can not be accessed" in error.message: # Login does not have permission to see Scripts, throw an informative # error how to work around this for now. raise UpdatePermissionsError() else: raise try: # try again after the initialization is done logger.debug("Importing sgtk after initialization") sgtk = __get_initialized_sgtk(core_path, app_bootstrap) if sgtk is None: # Generate a generic error message, which will suggest to contact support. raise Exception("Could not access API post initialization.") splash.set_message("Setting up default site configuration...") # Install the default site config sg = sgtk.util.shotgun.create_sg_connection() # Site config has a none project id. project_id = None # If no pipeline configuration had been found. if not pc: # This site config has never been set by anyone, so we're the first. # If pipeline configurations are still project entities, we'll have to use the # TemplateProject as the project which will host the pipeline configuration. if does_pipeline_configuration_require_project(connection): template_project = sg.find_one( "Project", [["name", "is", "Template Project"], ["layout_project", "is", None]]) # Can't find template project, so we're effectively done here, we need a project # to create a pipeline configuration. if template_project is None: # Generate a generic error message, which will suggest to contact support. raise Exception( "Error finding the Template project on your site.") logger.info( "Creating the site config using the template project.") # We'll need to use the template project's id to setup the site config in this case. project_id = template_project["id"] else: logger.info( "Creating the site config without using a project.") else: # If a project is set in the pipeline configuration, it's an old style site config tied # to the template project, so we have to use it. if pc.get("project") is not None: logger.info("Reusing the site config with a project.") project_id = pc["project"]["id"] else: logger.info("Reusing the site config without a project.") # Create the directory if not os.path.exists(default_site_config): os.makedirs(default_site_config) # Setup the command to create the config if sys.platform == "darwin": path_param = "config_path_mac" elif sys.platform == "win32": path_param = "config_path_win" elif sys.platform.startswith("linux"): path_param = "config_path_linux" # allow the config uri to be overridden for testing config_uri = os.environ.get("SGTK_SITE_CONFIG_DEBUG_LOCATION", "tk-config-site") params = { "auto_path": True, "config_uri": config_uri, "project_folder_name": "site", "project_id": project_id, path_param: default_site_config, } setup_project = sgtk.get_command("setup_project") setup_project.set_logger(logger) try: setup_project.execute(params) except Exception, error: logger.exception(error) if "CRUD ERROR" in error.message: raise UpdatePermissionsError() else: raise # and now try to load up sgtk through the config again sgtk = __get_initialized_sgtk(default_site_config, app_bootstrap) tk = sgtk.sgtk_from_path(default_site_config) # now localize the core to the config splash.set_message("Localizing core...") localize = tk.get_command("localize") localize.set_logger(logger) localize.execute({}) # Get back the pipeline configuration, this is expected to be initialized further down. _, pc = shotgun_desktop.paths.get_default_site_config_root( connection)
def __launch_app(app, splash, user, app_bootstrap, settings): """ Shows the splash screen, optionally downloads and configures Toolkit, imports it, optionally updates it and then launches the desktop engine. :param app: Application object for event processing. :param splash: Splash dialog to update user on what is currently going on :param user: Current ShotgunUser. :param app_bootstrap: Application bootstrap. :param settings: The application's settings. :returns: The error code to return to the shell. """ # show the splash screen splash.show() import sgtk sgtk.set_authenticated_user(user) # Downloads an upgrade for the startup if available. startup_updated = upgrade_startup( splash, sgtk, app_bootstrap ) if startup_updated: __restart_app_with_countdown(splash, "Shotgun Desktop updated.") splash.set_message("Looking up site configuration.") connection = user.create_sg_connection() _assert_toolkit_enabled(splash, connection) logger.debug("Getting the default site configuration.") pc_path, pc, toolkit_classic_required = shotgun_desktop.paths.get_pipeline_configuration_info(connection) # We need to toggle the global debug logging setting back prior to swapping # core. Cores older than v0.18.117 do not manage the TK_DEBUG environment # variable when global debug is toggled, so we can end up in a situation # where the new core in desktopstartup toggles it on, setting the env var, # and then when the toggle off occurs we end up with the older core in the # site config NOT purging the TK_DEBUG env var. The result is that any # subprocesses spawned then end up with debug logging on when the user # didn't ask for it. This impacts engine command execution from both Desktop # and via browser integration. It is the biggest issue with browser # integration, however, as the debug logs are reported back to the web # app from the desktopserver RPC API, and the user is presented with a # dialog full of debug log messages. __restore_global_debug_flag() # We're about to bootstrap, so remove sgtk from our scope so that if we add # code that uses it after the bootstrap we have to import the # new core. del sgtk if toolkit_classic_required: engine = __start_engine_in_toolkit_classic(app, splash, user, pc, pc_path) else: engine = __start_engine_in_zero_config(app, app_bootstrap, splash, user) return __post_bootstrap_engine(splash, app_bootstrap, engine, settings)
def __launch_app(app, splash, connection, app_bootstrap, server): """ Shows the splash screen, optionally downloads and configures Toolkit, imports it, optionally updates it and then launches the desktop engine. :param app: Application object for event processing. :param splash: Splash dialog to update user on what is currently going on :param connection: Connection to the Shotgun server. :param server: The tk_framework_desktopserver.Server instance. :returns: The error code to return to the shell. """ # show the splash screen splash.show() splash.set_message("Looking up site configuration.") app.processEvents() _assert_toolkit_enabled(splash, connection) logger.debug("Getting the default site config") default_site_config, pc = shotgun_desktop.paths.get_default_site_config_root(connection) # try and import toolkit toolkit_imported = False config_folder_exists_at_startup = os.path.exists(default_site_config) reset_site = __extract_command_line_argument("--reset-site") # If the config folder exists at startup but the user wants to wipe it, do it. if config_folder_exists_at_startup and reset_site: logger.info("Resetting site configuration at '%s'" % default_site_config) splash.set_message("Resetting site configuration ...") shutil.rmtree(default_site_config) # It doesn't exist anymore, so we can act as if it never existed in the first place config_folder_exists_at_startup = False # Remove all occurances of --reset-site so that if we restart the app it doesn't reset it # again. # If there is no pipeline configuration but we found something on disk nonetheless. if not pc and is_toolkit_already_configured(default_site_config): raise UnexpectedConfigFound(default_site_config) try: # In we found a pipeline configuration and the path for the config exists, try to import # Toolkit. if config_folder_exists_at_startup: logger.info("Trying site config from '%s'" % default_site_config) sgtk = __import_sgtk_from_path(default_site_config) toolkit_imported = True except Exception: logger.exception("There was an error importing Toolkit:") pass else: # Toolkit was imported, we need to initialize it now. if toolkit_imported: __initialize_sgtk_authentication(sgtk, app_bootstrap) if not toolkit_imported: # sgtk not available. initialize core logger.info("Import sgtk from site config failed. ") try: app.processEvents() splash.set_message("Initializing Toolkit") logger.info("Initializing Toolkit") core_path = initialize(splash, connection) except Exception, error: logger.exception(error) if "ApiUser can not be accessed" in error.message: # Login does not have permission to see Scripts, throw an informative # error how to work around this for now. raise UpdatePermissionsError() else: raise try: # try again after the initialization is done logger.debug("Importing sgtk after initialization") sgtk = __get_initialized_sgtk(core_path, app_bootstrap) if sgtk is None: # Generate a generic error message, which will suggest to contact support. raise Exception("Could not access API post initialization.") splash.set_message("Setting up default site configuration...") # Install the default site config sg = sgtk.util.shotgun.create_sg_connection() # Site config has a none project id. project_id = None # If no pipeline configuration had been found. if not pc: # This site config has never been set by anyone, so we're the first. # If pipeline configurations are still project entities, we'll have to use the # TemplateProject as the project which will host the pipeline configuration. if does_pipeline_configuration_require_project(connection): template_project = sg.find_one( "Project", [["name", "is", "Template Project"], ["layout_project", "is", None]]) # Can't find template project, so we're effectively done here, we need a project # to create a pipeline configuration. if template_project is None: # Generate a generic error message, which will suggest to contact support. raise Exception("Error finding the Template project on your site.") logger.info("Creating the site config using the template project.") # We'll need to use the template project's id to setup the site config in this case. project_id = template_project["id"] else: logger.info("Creating the site config without using a project.") else: # If a project is set in the pipeline configuration, it's an old style site config tied # to the template project, so we have to use it. if pc.get("project") is not None: logger.info("Reusing the site config with a project.") project_id = pc["project"]["id"] else: logger.info("Reusing the site config without a project.") # Create the directory if not os.path.exists(default_site_config): os.makedirs(default_site_config) # Setup the command to create the config if sys.platform == "darwin": path_param = "config_path_mac" elif sys.platform == "win32": path_param = "config_path_win" elif sys.platform.startswith("linux"): path_param = "config_path_linux" # allow the config uri to be overridden for testing config_uri = os.environ.get("SGTK_SITE_CONFIG_DEBUG_LOCATION", "tk-config-site") params = { "auto_path": True, "config_uri": config_uri, "project_folder_name": "site", "project_id": project_id, path_param: default_site_config, } setup_project = sgtk.get_command("setup_project") setup_project.set_logger(logger) try: setup_project.execute(params) except Exception, error: logger.exception(error) if "CRUD ERROR" in error.message: raise UpdatePermissionsError() else: raise # and now try to load up sgtk through the config again sgtk = __get_initialized_sgtk(default_site_config, app_bootstrap) tk = sgtk.sgtk_from_path(default_site_config) # now localize the core to the config splash.set_message("Localizing core...") localize = tk.get_command("localize") localize.set_logger(logger) localize.execute({}) # Get back the pipeline configuration, this is expected to be initialized further down. _, pc = shotgun_desktop.paths.get_default_site_config_root(connection)