def _load_default_environments(self, tools): """Load and apply default environment files.""" import acre os.environ['PLATFORM'] = platform.system().lower() tools_env = acre.get_tools(tools) pype_paths_env = dict() for key, value in dict(os.environ).items(): if key.startswith('PYPE_'): pype_paths_env[key] = value env = acre.append(tools_env, pype_paths_env) env = acre.compute(env, cleanup=True) os.environ = acre.append(dict(os.environ), env) os.environ = acre.compute(os.environ, cleanup=False)
def get_global_environments(env=None): """Load global environments from Pype. Return prepared and parsed global environments by pype's settings. Use combination of "global" environments set in pype's settings and enabled modules. Args: env (dict, optional): Initial environments. Empty dictionary is used when not entered. Returns; dict of str: Loaded and processed environments. """ import acre from openpype.modules import ModulesManager if env is None: env = {} # Get global environments from settings all_settings_env = get_environments() parsed_global_env = acre.parse(all_settings_env["global"]) # Merge with entered environments merged_env = acre.append(env, parsed_global_env) # Get environments from Pype modules modules_manager = ModulesManager() module_envs = modules_manager.collect_global_environments() publish_plugin_dirs = modules_manager.collect_plugin_paths()["publish"] # Set pyblish plugins paths if any module want to register them if publish_plugin_dirs: publish_paths_str = os.environ.get("PYBLISHPLUGINPATH") or "" publish_paths = publish_paths_str.split(os.pathsep) _publish_paths = { os.path.normpath(path) for path in publish_paths if path } for path in publish_plugin_dirs: _publish_paths.add(os.path.normpath(path)) module_envs["PYBLISHPLUGINPATH"] = os.pathsep.join(_publish_paths) # Merge environments with current environments and update values if module_envs: parsed_envs = acre.parse(module_envs) merged_env = acre.merge(parsed_envs, merged_env) return acre.compute(merged_env, cleanup=True)
def test_append(self): """Append paths of two environments into one.""" data_a = {"A": "A", "B": "B"} data_b = {"A": "A2", "C": "C2"} # Keep unaltered copies to check afterwards the originals # remain unaltered by the append function _data_a = data_a.copy() _data_b = data_b.copy() data = acre.append(data_a, data_b) self.assertEqual(data, {"A": "A;A2", "B": "B", "C": "C2"}) # Ensure the original dicts are unaltered self.assertEqual(data_a, _data_a) self.assertEqual(data_b, _data_b)
def launch(self, session, entities, event): '''Callback method for the custom action. return either a bool ( True if successful or False if the action failed ) or a dictionary with they keys `message` and `success`, the message should be a string and will be displayed as feedback to the user, success should be a bool, True if successful or False if the action failed. *session* is a `ftrack_api.Session` instance *entities* is a list of tuples each containing the entity type and the entity id. If the entity is a hierarchical you will always get the entity type TypedContext, once retrieved through a get operation you will have the "real" entity type ie. example Shot, Sequence or Asset Build. *event* the unmodified original event ''' entity = entities[0] project_name = entity['project']['full_name'] database = pypelib.get_avalon_database() # Get current environments env_list = [ 'AVALON_PROJECT', 'AVALON_SILO', 'AVALON_ASSET', 'AVALON_TASK', 'AVALON_APP', 'AVALON_APP_NAME' ] env_origin = {} for env in env_list: env_origin[env] = os.environ.get(env, None) # set environments for Avalon os.environ["AVALON_PROJECT"] = project_name os.environ["AVALON_SILO"] = entity['ancestors'][0]['name'] os.environ["AVALON_ASSET"] = entity['parent']['name'] os.environ["AVALON_TASK"] = entity['name'] os.environ["AVALON_APP"] = self.identifier.split("_")[0] os.environ["AVALON_APP_NAME"] = self.identifier anatomy = Anatomy() hierarchy = "" parents = database[project_name].find_one({ "type": 'asset', "name": entity['parent']['name'] })['data']['parents'] if parents: hierarchy = os.path.join(*parents) application = avalonlib.get_application(os.environ["AVALON_APP_NAME"]) data = { "root": os.environ.get("PYPE_STUDIO_PROJECTS_MOUNT"), "project": { "name": entity['project']['full_name'], "code": entity['project']['name'] }, "task": entity['name'], "asset": entity['parent']['name'], "app": application["application_dir"], "hierarchy": hierarchy, } av_project = database[project_name].find_one({"type": 'project'}) templates = None if av_project: work_template = av_project.get('config', {}).get('template', {}).get('work', None) work_template = None try: work_template = work_template.format(**data) except Exception: try: anatomy = anatomy.format(data) work_template = anatomy["work"]["folder"] except Exception as exc: msg = "{} Error in anatomy.format: {}".format( __name__, str(exc)) self.log.error(msg, exc_info=True) return {'success': False, 'message': msg} workdir = os.path.normpath(work_template) os.environ["AVALON_WORKDIR"] = workdir try: os.makedirs(workdir) except FileExistsError: pass # collect all parents from the task parents = [] for item in entity['link']: parents.append(session.get(item['type'], item['id'])) # collect all the 'environment' attributes from parents tools_attr = [os.environ["AVALON_APP"], os.environ["AVALON_APP_NAME"]] for parent in reversed(parents): # check if the attribute is empty, if not use it if parent['custom_attributes']['tools_env']: tools_attr.extend(parent['custom_attributes']['tools_env']) break tools_env = acre.get_tools(tools_attr) env = acre.compute(tools_env) env = acre.merge(env, current_env=dict(os.environ)) env = acre.append(dict(os.environ), env) # # tools_env = acre.get_tools(tools) # env = acre.compute(dict(tools_env)) # env = acre.merge(env, dict(os.environ)) # os.environ = acre.append(dict(os.environ), env) # os.environ = acre.compute(os.environ) # Get path to execute st_temp_path = os.environ['PYPE_CONFIG'] os_plat = platform.system().lower() # Path to folder with launchers path = os.path.join(st_temp_path, 'launchers', os_plat) # Full path to executable launcher execfile = None if sys.platform == "win32": for ext in os.environ["PATHEXT"].split(os.pathsep): fpath = os.path.join(path.strip('"'), self.executable + ext) if os.path.isfile(fpath) and os.access(fpath, os.X_OK): execfile = fpath break pass # Run SW if was found executable if execfile is not None: avalonlib.launch(executable=execfile, args=[], environment=env) else: return { 'success': False, 'message': "We didn't found launcher for {0}".format(self.label) } pass if sys.platform.startswith('linux'): execfile = os.path.join(path.strip('"'), self.executable) if os.path.isfile(execfile): try: fp = open(execfile) except PermissionError as p: self.log.exception('Access denied on {0} - {1}'.format( execfile, p)) return { 'success': False, 'message': "Access denied on launcher - {}".format(execfile) } fp.close() # check executable permission if not os.access(execfile, os.X_OK): self.log.error( 'No executable permission on {}'.format(execfile)) return { 'success': False, 'message': "No executable permission - {}".format(execfile) } pass else: self.log.error('Launcher doesn\'t exist - {}'.format(execfile)) return { 'success': False, 'message': "Launcher doesn't exist - {}".format(execfile) } pass # Run SW if was found executable if execfile is not None: avalonlib.launch('/usr/bin/env', args=['bash', execfile], environment=env) else: return { 'success': False, 'message': "We didn't found launcher for {0}".format(self.label) } pass # Change status of task to In progress presets = config.get_presets()["ftrack"]["ftrack_config"] if 'status_update' in presets: statuses = presets['status_update'] actual_status = entity['status']['name'].lower() next_status_name = None for key, value in statuses.items(): if actual_status in value or '_any_' in value: if key != '_ignore_': next_status_name = key break if next_status_name is not None: try: query = 'Status where name is "{}"'.format( next_status_name) status = session.query(query).one() entity['status'] = status session.commit() except Exception: msg = ('Status "{}" in presets wasn\'t found on Ftrack' ).format(next_status_name) self.log.warning(msg) # Set origin avalon environments for key, value in env_origin.items(): if value == None: value = "" os.environ[key] = value return {'success': True, 'message': "Launching {0}".format(self.label)}
def launch(self, session, entities, event): """Callback method for the custom action. return either a bool (True if successful or False if the action failed) or a dictionary with they keys `message` and `success`, the message should be a string and will be displayed as feedback to the user, success should be a bool, True if successful or False if the action failed. *session* is a `ftrack_api.Session` instance *entities* is a list of tuples each containing the entity type and the entity id. If the entity is a hierarchical you will always get the entity type TypedContext, once retrieved through a get operation you will have the "real" entity type ie. example Shot, Sequence or Asset Build. *event* the unmodified original event """ entity = entities[0] project_name = entity["project"]["full_name"] database = pypelib.get_avalon_database() asset_name = entity["parent"]["name"] asset_document = database[project_name].find_one({ "type": "asset", "name": asset_name }) hierarchy = "" asset_doc_parents = asset_document["data"].get("parents") if len(asset_doc_parents) > 0: hierarchy = os.path.join(*asset_doc_parents) application = avalon.lib.get_application(self.identifier) data = { "project": { "name": entity["project"]["full_name"], "code": entity["project"]["name"] }, "task": entity["name"], "asset": asset_name, "app": application["application_dir"], "hierarchy": hierarchy } try: anatomy = Anatomy(project_name) anatomy_filled = anatomy.format(data) workdir = os.path.normpath(anatomy_filled["work"]["folder"]) except Exception as exc: msg = "Error in anatomy.format: {}".format(str(exc)) self.log.error(msg, exc_info=True) return {"success": False, "message": msg} try: os.makedirs(workdir) except FileExistsError: pass # set environments for Avalon prep_env = copy.deepcopy(os.environ) prep_env.update({ "AVALON_PROJECT": project_name, "AVALON_ASSET": asset_name, "AVALON_TASK": entity["name"], "AVALON_APP": self.identifier.split("_")[0], "AVALON_APP_NAME": self.identifier, "AVALON_HIERARCHY": hierarchy, "AVALON_WORKDIR": workdir }) prep_env.update(anatomy.roots_obj.root_environments()) # collect all parents from the task parents = [] for item in entity['link']: parents.append(session.get(item['type'], item['id'])) # collect all the 'environment' attributes from parents tools_attr = [prep_env["AVALON_APP"], prep_env["AVALON_APP_NAME"]] tools_env = asset_document["data"].get("tools_env") or [] tools_attr.extend(tools_env) tools_env = acre.get_tools(tools_attr) env = acre.compute(tools_env) env = acre.merge(env, current_env=dict(prep_env)) env = acre.append(dict(prep_env), env) # Get path to execute st_temp_path = os.environ["PYPE_CONFIG"] os_plat = platform.system().lower() # Path to folder with launchers path = os.path.join(st_temp_path, "launchers", os_plat) # Full path to executable launcher execfile = None if application.get("launch_hook"): hook = application.get("launch_hook") self.log.info("launching hook: {}".format(hook)) ret_val = pypelib.execute_hook(application.get("launch_hook"), env=env) if not ret_val: return { 'success': False, 'message': "Hook didn't finish successfully {0}".format(self.label) } if sys.platform == "win32": for ext in os.environ["PATHEXT"].split(os.pathsep): fpath = os.path.join(path.strip('"'), self.executable + ext) if os.path.isfile(fpath) and os.access(fpath, os.X_OK): execfile = fpath break # Run SW if was found executable if execfile is None: return { "success": False, "message": "We didn't find launcher for {0}".format(self.label) } popen = avalon.lib.launch(executable=execfile, args=[], environment=env) elif (sys.platform.startswith("linux") or sys.platform.startswith("darwin")): execfile = os.path.join(path.strip('"'), self.executable) if not os.path.isfile(execfile): msg = "Launcher doesn't exist - {}".format(execfile) self.log.error(msg) return {"success": False, "message": msg} try: fp = open(execfile) except PermissionError as perm_exc: msg = "Access denied on launcher {} - {}".format( execfile, perm_exc) self.log.exception(msg, exc_info=True) return {"success": False, "message": msg} fp.close() # check executable permission if not os.access(execfile, os.X_OK): msg = "No executable permission - {}".format(execfile) self.log.error(msg) return {"success": False, "message": msg} # Run SW if was found executable if execfile is None: return { "success": False, "message": "We didn't found launcher for {0}".format(self.label) } popen = avalon.lib.launch( # noqa: F841 "/usr/bin/env", args=["bash", execfile], environment=env) # Change status of task to In progress presets = config.get_presets()["ftrack"]["ftrack_config"] if "status_update" in presets: statuses = presets["status_update"] actual_status = entity["status"]["name"].lower() already_tested = [] ent_path = "/".join([ent["name"] for ent in entity["link"]]) while True: next_status_name = None for key, value in statuses.items(): if key in already_tested: continue if actual_status in value or "_any_" in value: if key != "_ignore_": next_status_name = key already_tested.append(key) break already_tested.append(key) if next_status_name is None: break try: query = "Status where name is \"{}\"".format( next_status_name) status = session.query(query).one() entity["status"] = status session.commit() self.log.debug("Changing status to \"{}\" <{}>".format( next_status_name, ent_path)) break except Exception: session.rollback() msg = ("Status \"{}\" in presets wasn't found" " on Ftrack entity type \"{}\"").format( next_status_name, entity.entity_type) self.log.warning(msg) return {"success": True, "message": "Launching {0}".format(self.label)}