class SceneName(object): def __init__(self, **kwargs): # Reasonable defaults. self.detail = '' self.entity_name = '' self.entity_type = None self.extension = '' self.revision = 1 self.step_name = kwargs.get('step_name') self.sub_directory = '' self.directory = 'scenes' self.version = 0 self.sep = ',' self._all_seps_class = '[%s]' % re.escape('-_,.') self._strip_seps_re = re.compile( r'(^%s+)|(%s+$)' % (self._all_seps_class, self._all_seps_class)) self._sgfs = SGFS() # Callbacks. self.warning = kwargs.pop('warning', self.warning) self.error = kwargs.pop('error', self.error) if self.error is False: self.error = self.warning self._step_names = [] # Parse given paths. self.workspace = kwargs.pop('workspace', None) if self.workspace is not None: self._parse_workspace(self.workspace) self.filename = kwargs.pop('filename', None) if self.filename is not None: self._parse_filename(self.filename) # Set kwargs. self.detail = kwargs.pop('detail', self.detail) self.entity_name = kwargs.pop('entity_name', self.entity_name) self.entity_type = kwargs.pop('entity_type', self.entity_type) self.extension = kwargs.pop('extension', self.extension) self.revision = int(kwargs.pop('revision', self.revision)) self.step_name = kwargs.pop('step_name', self.step_name) # "scenes_name" one is for backwards compatibility. self.directory = kwargs.pop('directory', kwargs.pop('scenes_name', self.directory)) self.sub_directory = kwargs.pop('sub_directory', self.sub_directory) self.version = int(kwargs.pop('version', self.version)) if kwargs: raise TypeError(('%s recieved too many kwargs: ' % self.__class__.__name__) + ', '.join(kwargs)) def __repr__(self): return '<%s at 0x%x>' % (self.__class__.__name__, id(self)) def __str__(self): return self.get_path() def warning(self, message): print '# Warning:', message def error(self, message): raise ValueError(message) def _strip_seps(self, x): return self._strip_seps_re.sub('', x) def _split_workspace(self, workspace): tasks = self._sgfs.entities_from_path(workspace, ['Task']) if not tasks: self.error('No Tasks in current workspace') return # Incase error is not an exception. if len(tasks) > 1: warning_parts = [ '%s Tasks in current workspace; picking first of:' % len(tasks) ] for task in tasks: warning_parts.append(str(task)) self.warning('\n'.join(warning_parts)) task = tasks[0] try: task_workspace = self._sgfs.path_from_template( task, 'maya_workspace') except ValueError as e: self.warning('No maya_workspace template: %s' % e) task_workspace = os.path.join(self._sgfs.path_for_entity(task), 'maya') remaining = os.path.relpath(workspace, task_workspace) if remaining == os.path.curdir: remaining = '' return task, task_workspace, remaining def _parse_workspace(self, workspace, warn_on_remaining=True): task, task_workspace, remaining = self._split_workspace(workspace) if remaining.startswith(os.path.pardir): self.error( 'Entity not in workspace; SGFS seems broken! %s not in %s' % (task, workspace)) return if remaining and warn_on_remaining: self.warning('workspace may be too specific; %r remains' % remaining) entity = task.fetch('entity') self.entity_type = entity['type'] self.entity_name = entity.name self.step_name = task.fetch('step.Step.short_name') self.workspace = task_workspace self._step_names = [] def _parse_filename(self, filename): if os.path.isabs(filename): rel_filename = os.path.relpath(filename, self.workspace) if rel_filename.startswith('.'): self.warning('file not in workspace; %r not in %r' % (filename, self.workspace)) _, _, rel_filename = self._split_workspace(filename) else: rel_filename = filename # Extension filename, self.extension = os.path.splitext(rel_filename) directory = os.path.dirname(filename) filename = os.path.basename(filename) # Versions and revisions come out of the basename, and then the dirname m = re.search(r'v(\d+)', filename) or re.search(r'v(\d+)', directory) if m: self.version = int(m.group(1)) else: self.warning('Could not match version.') m = re.search(r'r(\d+)', filename) or re.search(r'r(\d+)', directory) if m: self.revision = int(m.group(1)) else: self.revision = 0 # Completely strip versioning out of the basename. filename = re.sub(r'[_]?[rv]\d+[_/]?', '', filename) filename = self._strip_seps(filename) # Assign (sub)directory around versioning. directory_parts = re.split(r'v\d+(?:/revisions?)?(?:/|$)', directory) if len(directory_parts) > 1: self.directory, self.sub_directory = directory_parts else: self.directory = directory # Strip entity name. if self.entity_name and filename.lower().startswith( self.entity_name.lower()): filename = filename[len(self.entity_name):] filename = self._strip_seps(filename) else: self.warning('Could not find shot/asset name prefix.') # Strip step name. if self.step_name and filename.lower().startswith( self.step_name.lower()): filename = filename[len(self.step_name):] filename = self._strip_seps(filename) else: self.warning('Could not find task/step prefix.') self.detail = filename def get_step_names(self): if self._step_names: return self._step_names step_dir = os.path.dirname(os.path.dirname(self.workspace)) try: for name in os.listdir(step_dir): # XXX: Hardcoded SGFS tag name?! if os.path.exists(os.path.join(step_dir, name, '.sgfs.yml')): self._step_names.append(name) except OSError: pass # Make sure we have a step name. if self.step_name is None: if not self._step_names: self.error('Could not identify pipeline step.') self._step_names = [''] self.step_name = self._step_names[0] # Make sure the step name is in step_names. self._step_names.append(self.step_name) self._step_names = sorted(set(self._step_names), key=lambda x: x.lower()) return self._step_names def get_basename(self): parts = [ self.entity_name, self.step_name, self.detail, 'v%04d' % self.version, 'r%04d' % self.revision if self.revision else None, ] parts = [x for x in parts if x] parts = [re.sub(r'[^\w-]+', '_', x) for x in parts] parts = [self._strip_seps(x) for x in parts] basename = self.sep.join(parts) return basename + self.extension def get_directory(self): path = os.path.join(self.workspace, self.directory) # Add '/v0001/revisions' if in an Asset and this is a maya scene. # Because the artists said so. That's why. if self.entity_type == 'Asset' and self.directory.startswith('scenes'): path = os.path.join(path, 'v' + '%04d' % self.version) if self.revision: path = os.path.join(path, 'revisions') path = os.path.join(path, self.sub_directory) return path def get_path(self): return os.path.join(self.get_directory(), self.get_basename())