def template_from_path(self, path): """ Finds a template that matches the given path:: >>> import sgtk >>> tk = sgtk.sgtk_from_path("/studio/project_root") >>> tk.template_from_path("/studio/my_proj/assets/Car/Anim/work") <Sgtk Template maya_asset_project: assets/%(Asset)s/%(Step)s/work> :param path: Path to match against a template :returns: :class:`TemplatePath` or None if no match could be found. """ matched_templates = self.templates_from_path(path) if len(matched_templates) == 0: return None elif len(matched_templates) == 1: return matched_templates[0] else: # ambiguity! # We're erroring out anyway, take the time to create helpful debug info! matched_fields = [] for template in matched_templates: matched_fields.append(template.get_fields(path)) msg = "%d templates are matching the path '%s'.\n" % ( len(matched_templates), path, ) msg += "The overlapping templates are:\n" for fields, template in zip(matched_fields, matched_templates): msg += "%s\n%s\n" % (template, fields) raise TankMultipleMatchingTemplatesError(msg)
def get_fields(self, input_path, skip_keys=None): """ Extracts key name, value pairs from a string. Example:: >>> input_path = '/studio_root/sgtk/demo_project_1/sequences/seq_1/shot_2/comp/publish/henry.v003.ma' >>> template_path.get_fields(input_path) {'Sequence': 'seq_1', 'Shot': 'shot_2', 'Step': 'comp', 'name': 'henry', 'version': 3} :param input_path: Source path for values :type input_path: String :param skip_keys: Optional keys to skip :type skip_keys: List :returns: Values found in the path based on keys in template :rtype: Dictionary """ path_parser = None fields = None for ordered_keys, static_tokens in zip(self._ordered_keys, self._static_tokens): path_parser = TemplatePathParser(ordered_keys, static_tokens) fields = path_parser.parse_path(input_path, skip_keys) if fields != None: break if fields is None: raise TankError("Template %s: %s" % (str(self), path_parser.last_error)) return fields
def __init__( self, name, default=None, choices=None, shotgun_entity_type=None, shotgun_field_name=None, exclusions=None, abstract=False, length=None, ): """ :param str name: Name by which the key will be referred. :param default: Default value for this key. If the default is a callable, it will be invoked without any parameters whenever a default value is required. :param choices: List of possible values for this key. Can be either a list or a dictionary of choice:label pairs. :param str shotgun_entity_type: For keys directly linked to a shotgun field, the entity type. :param str shotgun_field_name: For keys directly linked to a shotgun field, the field name. :param list exclusions: List of forbidden values. :param bool abstract: Flagging that this should be treated as an abstract key. :param int length: If non-None, indicating that the value should be of a fixed length. """ self._name = name self._default = default # special handling for choices: if isinstance(choices, dict): # new style choices dictionary containing choice:label pairs: self._choices = choices elif isinstance(choices, list) or isinstance(choices, set): # old style choices - labels and choices are the same: self._choices = dict(list(zip(choices, choices))) else: self._choices = {} self._exclusions = exclusions or [] self._shotgun_entity_type = shotgun_entity_type self._shotgun_field_name = shotgun_field_name self._is_abstract = abstract self._length = length self._last_error = "" # check that the key name doesn't contain invalid characters if not re.match(r"^%s$" % constants.TEMPLATE_KEY_NAME_REGEX, name): raise TankError("%s: Name contains invalid characters. " "Valid characters are %s." % (self, constants.VALID_TEMPLATE_KEY_NAME_DESC)) # Validation if self.shotgun_field_name and not self.shotgun_entity_type: raise TankError( "%s: Shotgun field requires a shotgun entity be set." % self) if self.is_abstract and self.default is None: raise TankError( "%s: Fields marked as abstract needs to have a default value!" % self) if not ((self.default is None) or self.validate(self.default)): raise TankError(self._last_error) if not all(self.validate(choice) for choice in self.choices): raise TankError(self._last_error)
def _unregister_filesystem_location_ids(self, ids, log, prompt): """ Performs the unregistration of a path from the path cache database. Will recursively unregister any child items parented to the given filesystem location id. :param ids: List of filesystem location ids to unregister :param log: Logging instance :param prompt: Should the user be presented with confirmation prompts? :returns: List of dictionaries to represents the items that were unregistered. Each dictionary has keys path and entity, where entity is a standard Shotgun-style link dictionary containing the keys type and id. Note that the shotgun ids returned will refer to retired objects in Shotgun rather than live ones. """ # tuple index constants for readability if len(ids) == 0: log.info("No associated folders found!") return [] # first of all, make sure we are up to date. pc = path_cache.PathCache(self.tk) try: pc.synchronize() finally: pc.close() # now use the path cache to get a list of all folders (recursively) that are # linked up to the folders registered for this entity. # store this in a set so that we ensure a unique set of matches paths = set() pc = path_cache.PathCache(self.tk) path_ids = [] paths = [] try: for sg_fs_id in ids: # get path subtree for this id via the path cache for path_obj in pc.get_folder_tree_from_sg_id(sg_fs_id): # store in the set as a tuple which is immutable paths.append(path_obj["path"]) path_ids.append(path_obj["sg_id"]) finally: pc.close() log.info("") log.info("The following folders will be unregistered:") for p in paths: log.info(" - %s" % p) log.info("") log.info( "Proceeding will unregister the above paths from Toolkit's path cache. " "This will not alter any of the content in the file system, but once you have " "unregistered the paths, they will not be recognized by Shotgun until you run " "Toolkit folder creation again.") log.info("") log.info( "This is useful if you have renamed an Asset or Shot and want to move its " "files to a new location on disk. In this case, start by unregistering the " "folders for the entity, then rename the Shot or Asset in Shotgun. " "Next, create new folders on disk using Toolkit's 'create folders' " "command. Finally, move the files to the new location on disk.") log.info("") if prompt: val = input( "Proceed with unregistering the above folders? (Yes/No) ? [Yes]: " ) if val != "" and not val.lower().startswith("y"): log.info("Exiting! Nothing was unregistered.") return [] log.info("Unregistering folders from Shotgun...") log.info("") path_cache.PathCache.remove_filesystem_location_entries( self.tk, path_ids) # lastly, another sync pc = path_cache.PathCache(self.tk) try: pc.synchronize() finally: pc.close() log.info("") log.info("Unregister complete. %s paths were unregistered." % len(paths)) # now shuffle the return data into a list of dicts return_data = [] for path_id, path in zip(path_ids, paths): return_data.append({ "path": path, "entity": { "type": path_cache.SHOTGUN_ENTITY, "id": path_id }, }) return return_data