def register_dcc_paths(dcc_paths=None): # Register DCC paths dccs_path = utils.force_list(dcc_paths) valid_dcc_paths = list() dcc_paths_str = '' default_dccs_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), 'dccs') dccs_path.append(default_dccs_path) for dcc_path in dccs_path: if os.path.isdir(dcc_path): if dcc_path not in sys.path: sys.path.append(dcc_path) valid_dcc_paths.append(dcc_path) if valid_dcc_paths: if os.environ.get(consts.AED, None): env = os.environ[consts.AED].split(';') env.extend(valid_dcc_paths) clean_env = list(set([utils.clean_path(pth) for pth in env])) dcc_paths_str = ';'.join(clean_env) else: dcc_paths_str = ';'.join(valid_dcc_paths) if os.environ.get(consts.AED, ''): if dcc_paths_str: os.environ[consts.AED] += ';{}'.format(dcc_paths_str) else: os.environ[consts.AED] = dcc_paths_str
def register_paths(plugin_paths): """ Registers given path into the list of Artella plugin paths. :param str plugin_paths: Path that will be used by Artella Plugins Manager to search plugins into. """ plugin_paths = utils.force_list(plugin_paths, remove_duplicates=True) for plugin_path in plugin_paths: plugin_path = utils.clean_path(plugin_path) if not plugin_path or not os.path.isdir( plugin_path) or plugin_path in _PLUGIN_PATHS: return _PLUGIN_PATHS.append(plugin_path)
def register_resources_path(resources_path): """ Registers a path where resources will be searched :param str resources_path: Path to search resources in """ if not resources_path or not os.path.isdir(resources_path): return resources_path = utils.clean_path(resources_path) if resources_path in _RESOURCES_PATHS: return _RESOURCES_PATHS.append(resources_path) dcc.register_dcc_resource_path(resources_path) icons_path = os.path.join(resources_path, 'icons') if os.path.isdir(icons_path): dcc.register_dcc_resource_path(icons_path)
def make_new_version(self, file_path=None, comment=None, do_lock=False): """ Uploads a new file/folder or a new version of current opened DCC scene file :param str file_path: Optional path of the file we want to create new version of. If not given, current opened DCC scene file path will be used. :param str comment: Optional comment to add to new version metadata. If not given, a generic message will be used. :param bool do_lock: Whether or not to force the lock of the file to make a new version. With new Artella version this is not mandatory. :return: True if the make new version operation is completed successfully; False otherwise. :rtype: bool """ artella_drive_client = self.get_client() if not artella_drive_client: return False if not file_path: file_path = dcc.scene_name() if not file_path: msg = 'Please open a file before creating a new version' logger.warning(msg) return False file_path = utils.clean_path(file_path) can_lock = artella_drive_client.can_lock_file(file_path=file_path) if not can_lock: msg = 'Unable to lock file to make new version. File is already locked by other user.' dcc.show_error('File already locked by other user', msg) logger.error(msg) return False version_created = True comment = str(comment) if comment else 'New file version' file_version = artella_drive_client.file_current_version(file_path) if file_version is None: self.show_warning_message('Unable to retrieve version from current scene') return False next_version = file_version + 1 is_locked, _, _, _ = artella_drive_client.check_lock(file_path) if not is_locked and do_lock: valid_lock = self.lock_file() if not valid_lock: self.show_error_message('Unable to lock file to make new version ({})'.format(next_version)) return False logger.info('Saving current scene: {}'.format(file_path)) valid_save = dcc.save_scene() if not valid_save: self.show_error_message('Unable to save current scene: "{}"'.format(file_path)) version_created = False else: uri_path = self.local_path_to_uri(file_path) rsp = artella_drive_client.upload(uri_path, comment=comment) if rsp.get('error'): msg = 'Unable to upload a new version of file: "{}"\n{}\n{}'.format( os.path.basename(file_path), rsp.get('url'), rsp.get('error')) self.show_error_message(msg) version_created = False if not is_locked and do_lock: self.unlock_file(show_dialogs=False) return version_created
def load_registered_plugins(dev=False): """ Loads all the plugins found in the registered plugin paths :return: """ plugin_paths = list( set([ utils.clean_path(plugin_path) for plugin_path in _PLUGIN_PATHS if os.path.isdir(plugin_path) ])) if not plugin_paths: logger.info('No Artella Plugins found to load!') return found_paths = dict() for plugin_path in plugin_paths: for root, dirs, files in os.walk(plugin_path): if consts.ARTELLA_PLUGIN_CONFIG not in files: continue clean_path = utils.clean_path(root) found_paths[clean_path] = os.path.join( root, consts.ARTELLA_PLUGIN_CONFIG) if not found_paths: logger.info('No plugins found in registered plugin paths: {}'.format( _PLUGIN_PATHS)) return for plugin_path in plugin_paths: for plugin_dir in os.listdir(plugin_path): clean_path = utils.clean_path(os.path.join(plugin_path, plugin_dir)) if os.path.isdir(clean_path) and clean_path not in sys.path: sys.path.append(clean_path) for plugin_path, plugin_config in found_paths.items(): # Search sub modules paths sub_modules_found = list() for sub_module in utils.iterate_modules(plugin_path): file_name = os.path.splitext(os.path.basename(sub_module))[0] if file_name.startswith('_') or file_name.startswith( 'test_') or sub_module.endswith('.pyc'): continue if not sub_module or sub_module in sub_modules_found: continue sub_modules_found.append(sub_module) if not sub_modules_found: continue # Find specific DCC plugin implementation dcc_name = dcc.name() sub_module = sub_modules_found[0] max_length = 50 index = 0 artella_module_parts = list() temp_sub_module = sub_module while True: if index > max_length: artella_module_parts = list() break base_name = os.path.basename(temp_sub_module) if not base_name or base_name == 'artella': artella_module_parts.append(base_name) break artella_module_parts.append(base_name) temp_sub_module = os.path.dirname(temp_sub_module) index += 1 if not artella_module_parts: module_path = utils.convert_module_path_to_dotted_path( os.path.normpath(sub_module)) else: module_path = os.path.splitext('.'.join( reversed(artella_module_parts)))[0] module_path_split = module_path.split('.') dcc_module_path = '{}.{}.{}'.format('.'.join(module_path_split[:-1]), dcc_name, module_path_split[-1]) sub_module_obj = utils.import_module(dcc_module_path, skip_exceptions=True) if not sub_module_obj: sub_module_obj = utils.import_module(module_path) if not sub_module_obj: logger.error( 'Error while importing Artella Plugin module: {}'.format( module_path)) continue if dev: plugin_version = 'DEV' else: plugin_version = None module_path_dir = module_path.rsplit('.', 1)[0] version_module_path = '{}.__version__'.format(module_path_dir) version_module_path = version_module_path.replace( '.{}.__'.format(dcc_name), '.__') try: version_module_obj = utils.import_module(version_module_path) except Exception: version_module_obj = None if version_module_obj: try: plugin_version = version_module_obj.get_version() except Exception as exc: logger.warning( 'Impossible to retrieve version for Artella Plugin module: {} | {}' .format(module_path, exc)) for member in utils.iterate_module_members(sub_module_obj, predicate=inspect.isclass): register_plugin(member[1], plugin_config, os.path.dirname(sub_module), plugin_version) if not _PLUGINS: logger.warning('No Artella plugins found to load!') return ordered_plugins_list = list() for plugin_id, plugin_dict in _PLUGINS.items(): plugin_class = plugin_dict['class'] plugin_index = plugin_class.INDEX or -1 index = 0 for i, plugin_item in enumerate(ordered_plugins_list): plugin_item_index = list(plugin_item.values())[0]['index'] if plugin_index < plugin_item_index: index += 1 ordered_plugins_list.insert( index, {plugin_id: { 'index': plugin_index, 'dict': plugin_dict }}) for plugin_item in ordered_plugins_list: plugin_id = list(plugin_item.keys())[0] plugin_dict = list(plugin_item.values())[0]['dict'] plugin_class = plugin_dict['class'] plugin_config_dict = plugin_dict.get('config', dict()) try: plugin_inst = plugin_class(plugin_config_dict) except Exception: logger.error( 'Impossible to instantiate Artella Plugin: "{}"'.format( plugin_id)) logger.error(traceback.format_exc()) continue _PLUGINS[plugin_id]['plugin_instance'] = plugin_inst
def uninstall(self, show_dialogs=True): artella_path = artella.__path__[0] if not os.path.isdir(artella_path): msg = 'Artella folder "{}" does not exists!'.format(artella_path) if show_dialogs: api.show_warning_message(text=msg) else: logger.warning(msg) return False res = qtutils.show_question_message_box( 'Artella Uninstaller', 'All plugins will be removed.\n\nArtella plugin will not be accessible by any DCC after uninstall.\n\n' 'Are you sure you want to uninstall Artella Plugin?') if not res: return False do_remove_install_folder = not dccplugin.DccPlugin().dev valid_uninstall = self._uninstall(artella_path) if not valid_uninstall: msg = 'Artella uninstall process was not completed!'.format(artella_path) if show_dialogs: api.show_error_message(text=msg) else: logger.error(msg) return False loader.shutdown(dev=False) if do_remove_install_folder: try: logger.info('Removing Artella Dcc Plugin directory: {}'.format(artella_path)) utils.delete_folder(artella_path) except Exception as exc: logger.warning( 'Impossible to remove Artella Dcc plugin directory: {} | {}'.format(artella_path, exc)) return False if os.path.isdir(artella_path): msg = 'Artella folder was not removed during uninstall process.\n\n{}\n\n Remove it manually if you ' \ 'want to have a complete clean uninstall of Artella plugin.'.format(artella_path) if show_dialogs: dcc.show_info('Artella Uninstaller', msg) else: logger.info(msg) utils.open_folder(os.path.dirname(artella_path)) # Remove specific DCC install folder if exists root_dcc_install_dir = os.path.dirname(os.path.dirname(os.path.dirname(artella_path))) dcc_install_dir = os.path.join(root_dcc_install_dir, dcc.name()) if os.path.isdir(dcc_install_dir): utils.delete_folder(dcc_install_dir) # Cleanup artella directories from artella_dir = os.path.dirname(artella_path) sys_paths = [artella_path, artella_dir, utils.clean_path(artella_path), utils.clean_path(artella_dir)] paths_to_remove = list() for sys_path in sys.path: if sys_path in sys_paths: paths_to_remove.append(sys_path) sys.path.remove(sys_path) elif 'artella-plugins' in sys_path or 'artella-dccs' in sys_path: paths_to_remove.append(sys_path) for path_to_remove in paths_to_remove: if path_to_remove not in sys.path: continue sys.path.remove(path_to_remove) return True
def current_dcc(): """ Returns the DCC loaded in current session for current environment If the current DCC is not already cached it will be automatically detected taking into account available DCC implementations. Otherwise, cached DCC is returned. :return: Name of the current used DCC :rtype: str """ # If active DCC is already cache, we use it global CURRENT_DCC global CURRENT_DCC_MODULE if CURRENT_DCC: return CURRENT_DCC # If DCC is not available we make sure that we check all available dirs where DCC implementation can be located global DCCS_DIRS global DCCS dcc_paths_str = os.environ.get(consts.AED, '') if dcc_paths_str: dcc_paths_split = [ utils.clean_path(pth) for pth in dcc_paths_str.split(';') ] for dcc_path in dcc_paths_split: if not dcc_path or not os.path.isdir( dcc_path) or dcc_path in DCCS_DIRS: continue DCCS_DIRS.append(dcc_path) if DCCS_DIRS: for dcc_dir in DCCS_DIRS: for dcc_folder in os.listdir(dcc_dir): if not dcc_folder.startswith('__') and not dcc_folder.endswith( '__') and dcc_folder != 'abstract': DCCS[dcc_folder] = utils.clean_path( os.path.join(dcc_dir, dcc_folder)) if DCCS[dcc_folder] not in sys.path: sys.path.append(DCCS[dcc_folder]) # Make sure standalone DCC is the last one checked dcc_names = list(DCCS.keys()) standalone_names = [ dcc_name for dcc_name in dcc_names if 'standalone' in dcc_name ] for standalone_name in standalone_names: dcc_names.pop(dcc_names.index(standalone_name)) dcc_names.append(standalone_name) # Loop through all available DCCs and check which one is available in current session for dcc_name in dcc_names: dcc_namespace_split = dcc_name.split('-') dcc_names = [dcc_name, dcc_namespace_split[-1] ] if dcc_namespace_split else [dcc_name] for dcc in dcc_names: if dcc.startswith('artella'): continue try: dcc_module_name = '{}.{}'.format(consts.ARTELLA_DCCS_NAMESPACE, dcc) import_module(dcc_module_name) CURRENT_DCC = dcc CURRENT_DCC_MODULE = dcc_module_name logger.info('Current DCC: {}'.format(CURRENT_DCC)) return CURRENT_DCC except ImportError as exc: continue