Beispiel #1
0
    def getPaddingNum(cls, chars, pad_style=PAD_STYLE_DEFAULT):
        """
        Given a supported group of padding characters, return the amount of padding.

        Args:
            chars (str): a supported group of padding characters
            pad_style (`.PAD_STYLE_DEFAULT` or `.PAD_STYLE_HASH1` or `.PAD_STYLE_HASH4`): padding style

        Returns:
            int:

        Raises:
            ValueError: if unsupported padding character is detected
        """
        if not chars:
            return 0

        match = PRINTF_SYNTAX_PADDING_RE.match(
            chars) or HOUDINI_SYNTAX_PADDING_RE.match(chars)
        if match:
            paddingNumStr = match.group(1)
            paddingNum = int(paddingNumStr) if paddingNumStr else 1
            return max(paddingNum, 1)

        try:
            rval = 0
            for char in chars:
                rval += cls.PAD_MAP[char][pad_style]
            return rval
        except KeyError:
            msg = "Detected an unsupported padding character: \"{}\"."
            msg += " Supported padding characters: {} or printf syntax padding"
            msg += " %<int>d"
            raise ValueError(
                msg.format(char, utils.asString(list(cls.PAD_MAP))))
Beispiel #2
0
    def frame(self, frame):
        """
        Return a path go the given frame in the sequence.  Integer or string
        digits are treated as a frame number and padding is applied, all other
        values are passed though.

        Examples:
            >>> seq.frame(1)
            /foo/bar.0001.exr
            >>> seq.frame("#")
            /foo/bar.#.exr

        Args:
            frame (int or str): the desired frame number or a char to pass
                through (ie. #)

        Returns:
            str:
        """
        try:
            zframe = utils.asString(int(frame)).zfill(self._zfill)
        except ValueError:
            zframe = frame

        # There may have been no placeholder for frame IDs in
        # the sequence, in which case we don't want to insert
        # a frame ID

        if self._zfill == 0:
            zframe = u""

        return u"".join((self._dir, self._base, zframe, self._ext))
Beispiel #3
0
    def isFrameRange(cls, frange):
        """
        Return True if the given string is a frame range. Any padding
        characters, such as '#' and '@' are ignored.

        Args:
            frange (str): a frame range to test

        Returns:
            bool:
        """
        # we're willing to trim padding characters from consideration
        # this translation is orders of magnitude faster than prior method
        if futils.PY2:
            frange = bytes(frange).translate(None, ''.join(cls.PAD_MAP.keys()))
        else:
            frange = str(frange)
            for key in cls.PAD_MAP:
                frange = frange.replace(key, '')

        if not frange:
            return True

        for part in asString(frange).split(','):
            if not part:
                continue
            try:
                cls._parse_frange_part(part)
            except ParseException:
                return False

        return True
Beispiel #4
0
    def __getitem__(self, idx):
        """
        Allows indexing and slicing into the underlying :class:`.FrameSet`

        When indexing, a string filepath is returns for the frame.

        When slicing, a new :class:`FileSequence` is returned.
        Slicing outside the range of the sequence results in an
        IndexError

        Args:
            idx (int or slice): the desired index

        Returns:
            str or :obj:`FileSequence`:

        Raises:
            :class:`IndexError`: If slice is outside the range of the sequence
        """
        if not self._frameSet:
            return utils.asString(self)

        frames = self._frameSet[idx]

        if not hasattr(idx, 'start'):
            return self.frame(frames)

        fset = FrameSet(frames)
        if fset.is_null:
            raise IndexError("slice is out of range and returns no frames")

        fs = self.copy()
        fs.setFrameSet(fset)
        return fs
Beispiel #5
0
        def finish_new_seq(seq):
            if seq._subframe_pad:
                seq._pad = '.'.join([seq._frame_pad, seq._subframe_pad])
            else:
                seq._pad = seq._frame_pad

            seq.__init__(utils.asString(seq), pad_style=pad_style, allow_subframes=allow_subframes)
Beispiel #6
0
    def getPaddingNum(chars):
        """
        Given a supported group of padding characters, return the amount of padding.

        Args:
            chars (str): a supported group of padding characters

        Returns:
            int:

        Raises:
            ValueError: if unsupported padding character is detected
        """
        match = PRINTF_SYNTAX_PADDING_RE.match(chars)
        if match:
            return int(match.group(1))

        try:
            rval = 0
            for char in chars:
                rval += PAD_MAP[char]
            return rval
        except KeyError:
            msg = "Detected an unsupported padding character: \"{}\"."
            msg += " Supported padding characters: {} or printf syntax padding"
            msg += " %<int>d"
            raise ValueError(msg.format(char, utils.asString(list(PAD_MAP))))
Beispiel #7
0
    def setBasename(self, base):
        """
        Set a new basename for the sequence.

        Args:
            base (str): the new base name
        """
        self._base = utils.asString(base)
Beispiel #8
0
    def setBasename(self, base):
        """
        Set a new basename for the sequence.

        Args:
            base (str): the new base name
        """
        self._base = utils.asString(base)
Beispiel #9
0
    def setBasename(self, base):
        """
        Set a new basename for the sequence.

        :type base: str
        :param base: the new base name
        :rtype: None
        """
        self._base = utils.asString(base)
Beispiel #10
0
    def setBasename(self, base):
        """
        Set a new basename for the sequence.

        :type base: str
        :param base: the new base name
        :rtype: None
        """
        self._base = utils.asString(base)
Beispiel #11
0
    def __str__(self):
        """
        String representation of this :class:`FileSequence`.

        Returns:
            str:
        """
        frameSet = utils.asString(self._frameSet or "")
        return "".join((self._dir, self._base, frameSet,
                        self._pad if frameSet else "", self._ext))
Beispiel #12
0
    def setExtension(self, ext):
        """
        Set a new file extension for the sequence.

        Note:
            A leading period will be added if none is provided.

        Args:
            ext (str): the new file extension
        """
        if ext[0] != ".":
            ext = "." + ext
        self._ext = utils.asString(ext)
Beispiel #13
0
    def setExtension(self, ext):
        """
        Set a new file extension for the sequence.

        .. note::
            A leading period will be added if none is provided.

        :param ext: the new file extension
        :rtype: None
        """
        if ext[0] != ".":
            ext = "." + ext
        self._ext = utils.asString(ext)
Beispiel #14
0
    def setExtension(self, ext):
        """
        Set a new file extension for the sequence.

        .. note::
            A leading period will be added if none is provided.

        :param ext: the new file extension
        :rtype: None
        """
        if ext[0] != ".":
            ext = "." + ext
        self._ext = utils.asString(ext)
Beispiel #15
0
    def setExtension(self, ext):
        """
        Set a new file extension for the sequence.

        Note:
            A leading period will be added if none is provided.

        Args:
            ext (str): the new file extension
        """
        if ext[0] != u".":
            ext = u"." + ext
        self._ext = utils.asString(ext)
Beispiel #16
0
    def setDirname(self, dirname):
        """
        Set a new directory name for the sequence.

        Args:
            dirname (str): the new directory name
        """
        # Make sure the dirname always ends in
        # a path separator character
        sep = utils._getPathSep(dirname)
        if not dirname.endswith(sep):
            dirname += sep

        self._dir = utils.asString(dirname)
Beispiel #17
0
    def __str__(self):
        """
        String representation of this :class:`FileSequence`.

        Returns:
            str:
        """
        frameSet = utils.asString(self._frameSet or u"")
        return u"".join((
            self._dir,
            self._base,
            frameSet,
            self._pad if frameSet else u"",
            self._ext))
Beispiel #18
0
    def __init__(self, sequence):
        """Init the class
        """
        sequence = utils.asString(sequence)

        if not hasattr(self, '_frameSet'):

            self._frameSet = None

            try:
                # the main case, padding characters in the path.1-100#.exr
                path, frames, self._pad, self._ext = SPLIT_RE.split(
                    sequence, 1)
                self._dir, self._base = os.path.split(path)
                self._frameSet = FrameSet(frames)
            except ValueError:
                # edge case 1; we've got an invalid pad
                for placeholder in PAD_MAP.keys():
                    if placeholder in sequence:
                        msg = "Failed to parse FileSequence: {0}"
                        raise ParseException(msg.format(sequence))
                # edge case 2; we've got a single frame of a sequence
                a_frame = DISK_RE.match(sequence)
                if a_frame:
                    self._dir, self._base, frames, self._ext = a_frame.groups()
                    # edge case 3: we've got a single versioned file, not a sequence
                    if frames and not self._base.endswith('.'):
                        self._base = self._base + frames
                        self._pad = ''
                    elif not frames:
                        self._pad = ''
                        self._frameSet = None
                    else:
                        self._frameSet = FrameSet(frames)
                        if self._frameSet:
                            self._pad = FileSequence.getPaddingChars(
                                len(frames))
                        else:
                            self._pad = ''
                            self._frameSet = None
                # edge case 4; we've got a solitary file, not a sequence
                else:
                    path, self._ext = os.path.splitext(sequence)
                    self._dir, self._base = os.path.split(path)
                    self._pad = ''

        if self._dir:
            self.setDirname(self._dir)

        self._zfill = self.__class__.getPaddingNum(self._pad)
Beispiel #19
0
    def setDirname(self, dirname):
        """
        Set a new directory name for the sequence.

        :type dirname: str
        :param dirname: the new directory name
        :rtype: None
        """
        # Make sure the dirname always ends in
        # a path separator character
        sep = utils._getPathSep(dirname)
        if not dirname.endswith(sep):
            dirname += sep

        self._dir = utils.asString(dirname)
Beispiel #20
0
    def setDirname(self, dirname):
        """
        Set a new directory name for the sequence.

        Args:
            dirname (str): the new directory name
        """
        # Make sure the dirname always ends in
        # a path separator character
        dirname = utils.asString(dirname)
        sep = utils._getPathSep(dirname)
        if not dirname.endswith(sep):
            dirname += sep

        self._dir = dirname
Beispiel #21
0
    def __init__(self, sequence):
        """Init the class
        """
        sequence = utils.asString(sequence)

        if not hasattr(self, '_frameSet'):

            self._frameSet = None

            try:
                # the main case, padding characters in the path.1-100#.exr
                path, frames, self._pad, self._ext = SPLIT_RE.split(sequence, 1)
                self._dir, self._base = os.path.split(path)
                self._frameSet = FrameSet(frames)
            except ValueError:
                # edge case 1; we've got an invalid pad
                for placeholder in PAD_MAP:
                    if placeholder in sequence:
                        msg = "Failed to parse FileSequence: {0}"
                        raise ParseException(msg.format(sequence))
                # edge case 2; we've got a single frame of a sequence
                a_frame = DISK_RE.match(sequence)
                if a_frame:
                    self._dir, self._base, frames, self._ext = a_frame.groups()
                    # edge case 3: we've got a single versioned file, not a sequence
                    if frames and not self._base.endswith('.'):
                        self._base = self._base + frames
                        self._pad = u''
                    elif not frames:
                        self._pad = u''
                        self._frameSet = None
                    else:
                        self._frameSet = FrameSet(frames)
                        if self._frameSet:
                            self._pad = FileSequence.getPaddingChars(len(frames))
                        else:
                            self._pad = u''
                            self._frameSet = None
                # edge case 4; we've got a solitary file, not a sequence
                else:
                    path, self._ext = os.path.splitext(sequence)
                    self._dir, self._base = os.path.split(path)
                    self._pad = u''

        if self._dir:
            self.setDirname(self._dir)

        self._zfill = self.__class__.getPaddingNum(self._pad)
Beispiel #22
0
    def __iter__(self):
        """
        Allow iteration over the path or paths this :class:`FileSequence`
        represents.

        Yields:
            :class:`FileSequence`:
        """
        # If there is no frame range, or there is no padding
        # characters, then we only want to represent a single path
        if not self._frameSet or not self._zfill:
            yield utils.asString(self)
            return

        for f in self._frameSet:
            yield self.frame(f)
Beispiel #23
0
    def __iter__(self):
        """
        Allow iteration over the path or paths this :class:`FileSequence`
        represents.

        Yields:
            :class:`FileSequence`:
        """
        # If there is no frame range, or there is no padding
        # characters, then we only want to represent a single path
        if not self._frameSet or not self._zfill:
            yield utils.asString(self)
            return

        for f in self._frameSet:
            yield self.frame(f)
Beispiel #24
0
    def __str__(self):
        """
        String representation of this :class:`FileSequence`.

        Returns:
            str:
        """
        frameSet = utils.asString(self._frameSet or "")
        parts = [
            self._dir,
            self._base,
            frameSet,
            self._pad if frameSet else "",
            self._ext,
        ]

        if futils.PY2:
            for i, part in enumerate(parts):
                if isinstance(part, futils.text_type):
                    parts[i] = futils.native(part.encode(utils.FILESYSTEM_ENCODING))

        return "".join(parts)
Beispiel #25
0
    def yield_sequences_in_list(paths):
        """
        Yield the discrete sequences within paths.  This does not try to
        determine if the files actually exist on disk, it assumes you already
        know that.

        Args:
            paths (list[str]): a list of paths

        Yields:
            :obj:`FileSequence`:
        """
        seqs = {}
        _check = DISK_RE.match

        for match in filter(None, map(_check, map(utils.asString, paths))):
            dirname, basename, frame, ext = match.groups()
            if not basename and not ext:
                continue
            key = (dirname, basename, ext)
            seqs.setdefault(key, set())
            if frame:
                seqs[key].add(frame)

        for (dirname, basename, ext), frames in iteritems(seqs):
            # build the FileSequence behind the scenes, rather than dupe work
            seq = FileSequence.__new__(FileSequence)
            seq._dir = dirname or u''
            seq._base = basename or u''
            seq._ext = ext or u''
            if frames:
                seq._frameSet = FrameSet(set(map(int, frames))) if frames else None
                seq._pad = FileSequence.getPaddingChars(min(map(len, frames)))
            else:
                seq._frameSet = None
                seq._pad = u''
            seq.__init__(utils.asString(seq))
            yield seq
Beispiel #26
0
    def yield_sequences_in_list(cls,
                                paths,
                                using=None,
                                pad_style=PAD_STYLE_DEFAULT,
                                allow_subframes=False):
        """
        Yield the discrete sequences within paths.  This does not try to
        determine if the files actually exist on disk, it assumes you already
        know that.

        A template :obj:`FileSequence` object can also be provided via the
        ``using`` parameter. Given this template, the dirname, basename, and
        extension values will be used to extract the frame value from the paths
        instead of parsing each path from scratch.

        Examples:
            The ``using`` field can supply a template for extracting the frame
            component from the paths::

                paths = [
                    '/dir/file_001.0001.ext',
                    '/dir/file_002.0001.ext',
                    '/dir/file_003.0001.ext',
                ]
                template = FileSequence('/dir/file_#.0001.ext')
                seqs = FileSequence.yield_sequences_in_list(paths, using)
                # [<FileSequence: '/dir/file_1-3@@@.0001.ext'>]

        Args:
            paths (list[str]): a list of paths
            using (:obj:`FileSequence`): Optional sequence to use as template
            pad_style (`.PAD_STYLE_DEFAULT` or `.PAD_STYLE_HASH1` or `.PAD_STYLE_HASH4`): padding style
            allow_subframes (bool): if True, handle subframe filenames

        Yields:
            :obj:`FileSequence`:
        """
        seqs = {}
        if allow_subframes:
            _check = cls.DISK_SUB_RE.match
        else:
            _check = cls.DISK_RE.match

        using_template = isinstance(using, FileSequence)

        if using_template:
            dirname, basename, ext = using.dirname(), using.basename(
            ), using.extension()
            head = len(dirname + basename)
            tail = -len(ext)
            frames = set()

            for path in filter(None, map(utils.asString, paths)):
                frame = path[head:tail]
                try:
                    int(frame)
                except ValueError:
                    if not allow_subframes:
                        continue
                    try:
                        decimal.Decimal(frame)
                    except decimal.DecimalException:
                        continue
                _, _, subframe = frame.partition(".")
                key = (dirname, basename, ext, len(subframe))
                seqs.setdefault(key, frames).add(frame)

        else:
            for match in filter(None, map(_check, map(utils.asString, paths))):
                dirname, basename, frame, ext = match.groups()
                if not basename and not ext:
                    continue
                if frame:
                    _, _, subframe = frame.partition(".")
                    key = (dirname, basename, ext, len(subframe))
                else:
                    key = (dirname, basename, ext, 0)
                seqs.setdefault(key, set())
                if frame:
                    seqs[key].add(frame)

        for (dirname, basename, ext,
             decimal_places), frames in iteritems(seqs):
            # build the FileSequence behind the scenes, rather than dupe work
            seq = cls.__new__(cls)
            seq._dir = dirname or ''
            seq._base = basename or ''
            seq._ext = ext or ''
            seq._pad_style = pad_style
            if frames:
                seq._frameSet = FrameSet(frames)

                frame_lengths = set()
                for frame in frames:
                    frame_num, _, _ = frame.partition(".")
                    frame_lengths.add(len(frame_num))

                seq._frame_pad = cls.getPaddingChars(min(frame_lengths),
                                                     pad_style=pad_style)
                if decimal_places:
                    seq._subframe_pad = cls.getPaddingChars(
                        decimal_places, pad_style=pad_style)
                else:
                    seq._subframe_pad = ''
            else:
                seq._frameSet = None
                seq._frame_pad = ''
                seq._subframe_pad = ''

            if seq._subframe_pad:
                seq._pad = '.'.join([seq._frame_pad, seq._subframe_pad])
            else:
                seq._pad = seq._frame_pad

            seq.__init__(utils.asString(seq))
            yield seq
Beispiel #27
0
    def __init__(self, frange):
        """Initialize the :class:`FrameSet` object.
        """

        # if the user provides anything but a string, short-circuit the build
        if not isinstance(frange, futils.string_types):
            # if it's apparently a FrameSet already, short-circuit the build
            if set(dir(frange)).issuperset(self.__slots__):
                for attr in self.__slots__:
                    setattr(self, attr, getattr(frange, attr))
                return
            # if it's inherently disordered, sort and build
            elif isinstance(frange, Set):
                self._maxSizeCheck(frange)
                self._items = frozenset(normalizeFrames(frange))
                self._order = tuple(sorted(self._items))
                self._frange = self.framesToFrameRange(
                    self._order, sort=False, compress=False)
                return
            # if it's ordered, find unique and build
            elif isinstance(frange, Sequence):
                self._maxSizeCheck(frange)
                items = set()
                order = unique(items, normalizeFrames(frange))
                self._order = tuple(order)
                self._items = frozenset(items)
                self._frange = self.framesToFrameRange(
                    self._order, sort=False, compress=False)
                return
            # if it's an individual number build directly
            elif isinstance(frange, futils.integer_types + (float, decimal.Decimal)):
                frame = normalizeFrame(frange)
                self._order = (frame, )
                self._items = frozenset([frame])
                self._frange = self.framesToFrameRange(
                    self._order, sort=False, compress=False)
            # in all other cases, cast to a string
            else:
                try:
                    frange = asString(frange)
                except Exception as err:
                    msg = 'Could not parse "{0}": cast to string raised: {1}'
                    raise ParseException(msg.format(frange, err))

        # we're willing to trim padding characters from consideration
        # this translation is orders of magnitude faster than prior method
        if futils.PY2:
            frange = bytes(frange).translate(None, ''.join(self.PAD_MAP.keys()))
            self._frange = asString(frange)
        else:
            frange = str(frange)
            for key in self.PAD_MAP:
                frange = frange.replace(key, '')
            self._frange = asString(frange)

        # because we're acting like a set, we need to support the empty set
        if not self._frange:
            self._items = frozenset()
            self._order = tuple()
            return

        # build the mutable stores, then cast to immutable for storage
        items = set()
        order = []

        maxSize = constants.MAX_FRAME_SIZE

        frange_parts = []
        frange_types = []
        for part in self._frange.split(","):
            # this is to deal with leading / trailing commas
            if not part:
                continue
            # parse the partial range
            start, end, modifier, chunk = self._parse_frange_part(part)
            frange_parts.append((start, end, modifier, chunk))
            frange_types.extend(map(type, (start, end, chunk)))

        # Determine best type for numbers in range. Note that
        # _parse_frange_part will always return decimal.Decimal for subframes
        FrameType = int
        if decimal.Decimal in frange_types:
            FrameType = decimal.Decimal

        for start, end, modifier, chunk in frange_parts:
            # handle batched frames (1-100x5)
            if modifier == 'x':
                frames = xfrange(start, end, chunk, maxSize=maxSize)
                frames = [FrameType(f) for f in frames if f not in items]
                self._maxSizeCheck(len(frames) + len(items))
                order.extend(frames)
                items.update(frames)
            # handle staggered frames (1-100:5)
            elif modifier == ':':
                if '.' in futils.native_str(chunk):
                    raise ValueError("Unable to stagger subframes")
                for stagger in range(chunk, 0, -1):
                    frames = xfrange(start, end, stagger, maxSize=maxSize)
                    frames = [f for f in frames if f not in items]
                    self._maxSizeCheck(len(frames) + len(items))
                    order.extend(frames)
                    items.update(frames)
            # handle filled frames (1-100y5)
            elif modifier == 'y':
                if '.' in futils.native_str(chunk):
                    raise ValueError("Unable to fill subframes")
                not_good = frozenset(xfrange(start, end, chunk, maxSize=maxSize))
                frames = xfrange(start, end, 1, maxSize=maxSize)
                frames = (f for f in frames if f not in not_good)
                frames = [f for f in frames if f not in items]
                self._maxSizeCheck(len(frames) + len(items))
                order.extend(frames)
                items.update(frames)
            # handle full ranges and single frames
            else:
                frames = xfrange(start, end, 1 if start < end else -1, maxSize=maxSize)
                frames = [FrameType(f) for f in frames if f not in items]
                self._maxSizeCheck(len(frames) + len(items))
                order.extend(frames)
                items.update(frames)

        # lock the results into immutable internals
        # this allows for hashing and fast equality checking
        self._items = frozenset(items)
        self._order = tuple(order)
Beispiel #28
0
    def __init__(self,
                 sequence,
                 pad_style=PAD_STYLE_DEFAULT,
                 allow_subframes=False):
        """Init the class
        """
        sequence = utils.asString(sequence)

        if not hasattr(self, '_frameSet'):

            self._frameSet = None

            if allow_subframes:
                split_re = self.SPLIT_SUB_RE
                disk_re = self.DISK_SUB_RE
            else:
                split_re = self.SPLIT_RE
                disk_re = self.DISK_RE

            try:
                # the main case, padding characters in the path.1-100#.exr
                path, frames, self._pad, self._ext = split_re.split(
                    sequence, 1)
                self._frame_pad, _, self._subframe_pad = self._pad.partition(
                    '.')
                self._dir, self._base = os.path.split(path)
                self._frameSet = FrameSet(frames)
            except ValueError:
                # edge case 1; we've got an invalid pad
                for placeholder in self.PAD_MAP:
                    if placeholder in sequence:
                        msg = "Failed to parse FileSequence: {!r}"
                        raise ParseException(msg.format(sequence))
                # edge case 2; we've got a single frame of a sequence
                a_frame = disk_re.match(sequence)
                if a_frame:
                    self._dir, self._base, frames, self._ext = a_frame.groups()
                    # edge case 3: we've got a single versioned file, not a sequence
                    if frames and not self._base.endswith('.'):
                        self._base = self._base + frames
                        self._pad = ''
                        self._frame_pad = ''
                        self._subframe_pad = ''
                    elif not frames:
                        self._pad = ''
                        self._frame_pad = ''
                        self._subframe_pad = ''
                        self._frameSet = None
                    else:
                        self._frameSet = FrameSet(frames)
                        if self._frameSet:
                            frame_num, _, subframe_num = frames.partition('.')
                            self._frame_pad = self.getPaddingChars(
                                len(frame_num), pad_style=pad_style)
                            if subframe_num:
                                self._subframe_pad = self.getPaddingChars(
                                    len(subframe_num), pad_style=pad_style)
                                self._pad = '.'.join(
                                    [self._frame_pad, self._subframe_pad])
                            else:
                                self._pad = self._frame_pad
                                self._subframe_pad = ''
                        else:
                            self._pad = ''
                            self._frame_pad = ''
                            self._subframe_pad = ''
                            self._frameSet = None
                # edge case 4; we've got a solitary file, not a sequence
                else:
                    path, self._ext = os.path.splitext(sequence)
                    self._dir, self._base = os.path.split(path)
                    self._pad = ''
                    self._frame_pad = ''
                    self._subframe_pad = ''

        if self._dir:
            self.setDirname(self._dir)

        self._pad_style = pad_style
        self._zfill = self.getPaddingNum(self._frame_pad, pad_style=pad_style)
        self._decimal_places = self.getPaddingNum(self._subframe_pad,
                                                  pad_style=pad_style)

        # Round subframes to match sequence
        if self._frameSet is not None and self._frameSet.hasSubFrames():
            self._frameSet = FrameSet([
                utils.quantize(frame, self._decimal_places)
                for frame in self._frameSet
            ])