def findSequenceOnDisk(pattern): """ Search for a specific sequence on disk. :Example: >>> findSequenceOnDisk("seq/bar#.exr") # or any fileseq pattern :param pattern: the sequence pattern being searched for :rtype: str :raises: :class:`fileseq.exceptions.FileSeqException` if no sequence is found on disk """ seq = FileSequence(pattern) if seq.frameRange() == '' and seq.padding() == '': if os.path.isfile(pattern): return seq patt = seq.format('{dirname}{basename}*{extension}') ext = seq.extension() basename = seq.basename() matches = FileSequence.yield_sequences_in_list(iglob(patt)) for match in matches: if match.basename() == basename and match.extension() == ext: return match msg = 'no sequence found on disk matching {0}' raise FileSeqException(msg.format(pattern))
def findSequenceOnDisk(cls, pattern, strictPadding=False): """ Search for a specific sequence on disk. The padding characters used in the `pattern` are used to filter the frame values of the files on disk (if `strictPadding` is True). Examples: Find sequence matching basename and extension, and a wildcard for any frame. returns bar.1.exr bar.10.exr, bar.100.exr, bar.1000.exr, inclusive >>> findSequenceOnDisk("seq/bar@@@@.exr") Find exactly 4-padded sequence, i.e. seq/bar1-100#.exr returns only frames bar1000.exr through bar9999.exr >>> findSequenceOnDisk("seq/bar#.exr", strictPadding=True) Args: pattern (str): the sequence pattern being searched for strictPadding (bool): if True, ignore files with padding length different from `pattern` Returns: str: Raises: :class:`.FileSeqException`: if no sequence is found on disk """ seq = cls(pattern) if seq.frameRange() == '' and seq.padding() == '': if os.path.isfile(pattern): return seq patt = seq.format('{dirname}{basename}*{extension}') ext = seq.extension() basename = seq.basename() pad = seq.padding() globbed = iglob(patt) if pad and strictPadding: globbed = cls._filterByPaddingNum(globbed, seq.zfill()) pad = cls.conformPadding(pad) matches = cls.yield_sequences_in_list(globbed) for match in matches: if match.basename() == basename and match.extension() == ext: if pad and strictPadding: match.setPadding(pad) return match msg = 'no sequence found on disk matching {0}' raise FileSeqException(msg.format(pattern))
def getPaddingChars(cls, num, pad_style=PAD_STYLE_DEFAULT): """ Given a particular amount of padding, return the proper padding characters. Args: num (int): required width of string with padding pad_style (`.PAD_STYLE_DEFAULT` or `.PAD_STYLE_HASH1` or `.PAD_STYLE_HASH4`): padding style Returns: str: """ num = max(1, num) reverse_pad_map = cls.REVERSE_PAD_MAP[pad_style] # Find the widest padding character that can be used alone for width in sorted(reverse_pad_map, reverse=True): if num % width == 0: return reverse_pad_map[width] * (num // width) # Should never reach here as all styles should have an entry for width 1 raise FileSeqException('REVERSE_PAD_MAP missing pad character for width 1')
def findSequencesOnDisk(cls, pattern, include_hidden=False, strictPadding=False, pad_style=PAD_STYLE_DEFAULT, allow_subframes=False): """ Yield the sequences found in the given directory. Examples:: FileSequence.findSequencesOnDisk('/path/to/files') The `pattern` can also specify glob-like shell wildcards including the following: * ``?`` - 1 wildcard character * ``*`` - 1 or more wildcard character * ``{foo,bar}`` - either 'foo' or 'bar' Exact frame ranges are not considered, and padding characters are converted to wildcards (``#`` or ``@``) Examples:: FileSequence.findSequencesOnDisk('/path/to/files/image_stereo_{left,right}.#.jpg') FileSequence.findSequencesOnDisk('/path/to/files/imag?_*_{left,right}.@@@.jpg', strictPadding=True) Args: pattern (str): directory to scan, or pattern to filter in directory include_hidden (bool): if true, show .hidden files as well strictPadding (bool): if True, ignore files with padding length different from pattern pad_style (`.PAD_STYLE_DEFAULT` or `.PAD_STYLE_HASH1` or `.PAD_STYLE_HASH4`): padding style allow_subframes (bool): if True, handle subframe filenames Returns: list: """ # reserve some functions we're going to need quick access to _not_hidden = lambda f: not f.startswith('.') _match_pattern = None _filter_padding = None _join = os.path.join seq = None dirpath = pattern # Support the pattern defining a filter for the files # in the existing directory if not os.path.isdir(pattern): dirpath, filepat = os.path.split(pattern) if not os.path.isdir(dirpath): return [] # Start building a regex for filtering files seq = cls(filepat, pad_style=pad_style, allow_subframes=allow_subframes) patt = r'\A' patt += cls._globCharsToRegex(seq.basename()) if seq.padding(): patt += '(' if seq.framePadding(): patt += r'\d+' if seq.subframePadding(): patt += r'\.\d+' patt += ')' if seq.extension(): patt += cls._globCharsToRegex(seq.extension()) # Convert braces groups into regex capture groups matches = re.finditer(r'{(.*?)(?:,(.*?))*}', patt) for match in reversed(list(matches)): i, j = match.span() regex = '(?:%s)' % '|'.join( [m.strip() for m in match.groups()]) patt = "".join((patt[0:i], regex, patt[j:])) patt += r'\Z' try: _match_pattern = re.compile(patt).match except re.error: msg = 'Invalid file pattern: {!r}'.format(filepat) raise FileSeqException(msg) if seq.padding() and strictPadding: get_frame = lambda f: _match_pattern(f).group(1) _filter_padding = functools.partial( cls._filterByPaddingNum, zfill=seq.zfill(), decimal_places=seq.decimalPlaces(), get_frame=get_frame) # Get just the immediate files under the dir. # Avoids testing the os.listdir() for files as # a second step. ret = next(os.walk(dirpath), None) files = ret[-1] if ret else [] # collapse some generators to get us the files that match our regex if not include_hidden: files = filter(_not_hidden, files) # Filter by files that match the provided file pattern if _match_pattern: files = filter(_match_pattern, files) # Filter by files that match the frame padding in the file pattern if _filter_padding: # returns a generator files = _filter_padding(files) # Ensure our dirpath ends with a path separator, so # that we can control which sep is used during the # os.path.join sep = utils._getPathSep(dirpath) if not dirpath.endswith(sep): dirpath += sep files = [_join(dirpath, f) for f in files] seqs = list( cls.yield_sequences_in_list(files, pad_style=pad_style, allow_subframes=allow_subframes)) if _filter_padding and seq: frame_pad = cls.conformPadding(seq.framePadding(), pad_style=pad_style) subframe_pad = cls.conformPadding(seq.subframePadding(), pad_style=pad_style) # strict padding should preserve the original padding # characters in the found sequences. for s in seqs: s.setFramePadding(frame_pad) s.setSubframePadding(subframe_pad) return seqs
def findSequenceOnDisk(cls, pattern, strictPadding=False, pad_style=PAD_STYLE_DEFAULT, allow_subframes=False): """ Search for a specific sequence on disk. The padding characters used in the `pattern` are used to filter the frame values of the files on disk (if `strictPadding` is True). Examples: Find sequence matching basename and extension, and a wildcard for any frame. returns bar.1.exr bar.10.exr, bar.100.exr, bar.1000.exr, inclusive ``FileSequence.findSequenceOnDisk("seq/bar@@@@.exr")`` Find exactly 4-padded sequence, i.e. seq/bar1-100#.exr returns only frames bar1000.exr through bar9999.exr ``FileSequence.findSequenceOnDisk("seq/bar#.exr", strictPadding=True)`` Args: pattern (str): the sequence pattern being searched for strictPadding (bool): if True, ignore files with padding length different from `pattern` pad_style (`.PAD_STYLE_DEFAULT` or `.PAD_STYLE_HASH1` or `.PAD_STYLE_HASH4`): padding style allow_subframes (bool): if True, handle subframe filenames Returns: str: Raises: :class:`.FileSeqException`: if no sequence is found on disk """ seq = cls(pattern, allow_subframes=allow_subframes, pad_style=pad_style) if seq.frameRange() == '' and seq.padding() == '': if os.path.isfile(pattern): return seq patt = seq.format('{dirname}{basename}*{extension}') dirname = seq.dirname() basename = seq.basename() ext = seq.extension() pad = seq.padding() frame_pad = seq.framePadding() subframe_pad = seq.subframePadding() globbed = iglob(patt) if pad: patt = r'\A' if dirname: patt = r'.*[/\\]' patt += re.escape(basename) + '(.*)' + re.escape(ext) + r'\Z' get_frame = lambda f: re.match(patt, f).group(1) if strictPadding: globbed = cls._filterByPaddingNum( globbed, seq.zfill(), decimal_places=seq.decimalPlaces(), get_frame=get_frame) frame_pad = cls.conformPadding(frame_pad, pad_style=pad_style) subframe_pad = cls.conformPadding(subframe_pad, pad_style=pad_style) else: globbed = cls._filterByPaddingNum( globbed, None, decimal_places=seq.decimalPlaces(), get_frame=get_frame) sequences = [] allow_subframes = bool(seq.decimalPlaces()) for match in cls.yield_sequences_in_list( globbed, using=seq, pad_style=pad_style, allow_subframes=allow_subframes): if match.basename() == basename and match.extension() == ext: if pad and strictPadding: match.setFramePadding(frame_pad) match.setSubframePadding(subframe_pad) sequences.append(match) if len(sequences) == 1: return sequences[0] elif not sequences: msg = 'no sequence found on disk matching {0}' else: msg = 'multiple sequences found on disk matching {0}' raise FileSeqException(msg.format(pattern))
def findSequencesOnDisk(cls, pattern, include_hidden=False, strictPadding=False): """ Yield the sequences found in the given directory. Example:: findSequencesOnDisk('/path/to/files') The pattern can also specify glob-like shell wildcards including the following: ? - 1 wildcard character * - 1 or more wildcard character {foo,bar} - either 'foo' or 'bar' Exact frame ranges are not considered, and padding characters are converted to wildcards (# or @) Example:: findSequencesOnDisk('/path/to/files/image_stereo_{left,right}.#.jpg') findSequencesOnDisk('/path/to/files/imag?_*_{left,right}.@@@.jpg', strictPadding=True) :param pattern: directory to scan, or pattern to filter in directory :type include_hidden: bool :param include_hidden: if true, show .hidden files as well :type strictPadding: bool :param strictPadding: if True, ignore files with padding length different from pattern :rtype: list """ # reserve some functions we're going to need quick access to _not_hidden = lambda f: not f.startswith('.') _match_pattern = None _filter_padding = None _join = os.path.join dirpath, filepat = pattern, None # Support the pattern defining a filter for the files # in the existing directory if not os.path.isdir(pattern): dirpath, filepat = os.path.split(pattern) if not os.path.isdir(dirpath): return [] # Start building a regex for filtering files seq = cls(filepat) patt = seq.basename().replace('.', r'\.') if seq.padding(): patt += '\d+' if seq.extension(): patt += seq.extension() # Convert braces groups into regex capture groups view = bytearray(patt) matches = re.finditer(r'{(.*?)(?:,(.*?))*}', patt) for match in reversed(list(matches)): i, j = match.span() view[i:j] = '(%s)' % '|'.join( [m.strip() for m in match.groups()]) view = view.replace('*', '.*') view = view.replace('?', '.') view += '$' print(view) try: _match_pattern = re.compile(str(view)).match except re.error: msg = 'Invalid file pattern: {}'.format(filepat) raise FileSeqException(msg) if seq.padding() and strictPadding: _filter_padding = functools.partial(cls._filterByPaddingNum, num=seq.zfill()) # Get just the immediate files under the dir. # Avoids testing the os.listdir() for files as # a second step. ret = next(os.walk(dirpath), None) files = ret[-1] if ret else [] # collapse some generators to get us the files that match our regex if not include_hidden: files = filter(_not_hidden, files) # Filter by files that match the provided file pattern if _match_pattern: files = filter(_match_pattern, files) # Filter by files that match the frame padding in the file pattern if _filter_padding: # returns a generator files = _filter_padding(files) # Ensure our dirpath ends with a path separator, so # that we can control which sep is used during the # os.path.join sep = utils._getPathSep(dirpath) if not dirpath.endswith(sep): dirpath += sep files = (_join(dirpath, f) for f in files) files = list(files) return list(FileSequence.yield_sequences_in_list(files))