docs: def OpenDiscreteVersions(self,
                             urn,
                             mode="r",
                             token=None,
                             local_cache=None,
                             age=ALL_TIMES,
                             diffs_only=False,
                             follow_symlinks=True):
      """Returns all the versions of the object as AFF4 objects.
  
      This iterates through versions of an object, returning the newest version
      first, then each older version until the beginning of time.
  
      Note that versions are defined by changes to the TYPE attribute, and this
      takes the version between two TYPE attributes.
      In many cases as a user you don't want this, as you want to be returned an
      object with as many attributes as possible, instead of the subset of them
      that were Set between these two times.
  
      Args:
        urn: The urn to open.
        mode: The mode to open the file with.
        token: The Security Token to use for opening this item.
        local_cache: A dict containing a cache as returned by GetAttributes. If
          set, this bypasses the factory cache.
        age: The age policy used to build this object. Should be one of ALL_TIMES
          or a time range
        diffs_only: If True, will return only diffs between the versions (instead
          of full objects).
        follow_symlinks: If object opened is a symlink, follow it.
  
      Yields:
        An AFF4Object for each version.
  
      Raises:
        IOError: On bad open or wrong time range specified.
      """
      if age == NEWEST_TIME or len(age) == 1:
        raise IOError("Bad age policy NEWEST_TIME for OpenDiscreteVersions.")
  
      aff4object = FACTORY.Open(
          urn,
          mode=mode,
          token=token,
          local_cache=local_cache,
          age=age,
          follow_symlinks=follow_symlinks)
  
      # TYPE is always written last so we trust it to bound the version.
      # Iterate from newest to oldest.
      type_iter = aff4object.GetValuesForAttribute(aff4object.Schema.TYPE)
      versions = sorted(t.age for t in type_iter)
  
      additional_aff4object = None
      # If we want full versions of the object, age != ALL_TIMES and we're
      # dealing with a time range with a start != 0, then we need to fetch
      # the version of the object corresponding to the start of the time
      # range and use its attributes when doing filtering below.
      #
      # Same applies to fetching in DIFF mode and passing a time range in "age".
      # Unversioned arguments will have a timestamp zero and won't be included
      # into the diff. additional_aff4object is then used to account for them.
      if len(age) == 2 and age[0]:
        additional_aff4object = FACTORY.Open(
            urn, mode=mode, token=token, local_cache=local_cache, age=age[0])
  
      if diffs_only:
        versions.insert(0, rdfvalue.RDFDatetime(0))
        pairs = list(zip(versions, versions[1:]))
      else:
        pairs = [(rdfvalue.RDFDatetime(0), v) for v in versions]
  
      if pairs:
        right_end = pairs[-1][1]
  
      for start, end in pairs:
        # Create a subset of attributes for use in the new object that represents
        # this version.
        clone_attrs = {}
  
        if additional_aff4object:
          # Attributes are supposed to be in the decreasing timestamp
          # order (see aff4.FACTORY.GetAttributes as a reference). Therefore
          # appending additional_aff4object to the end (since it corresponds
          # to the earliest version of the object).
          attributes = itertools.chain(
              iteritems(aff4object.synced_attributes),
              iteritems(additional_aff4object.synced_attributes))
        else:
          attributes = iteritems(aff4object.synced_attributes)
  
        for k, values in attributes:
          reduced_v = []
          for v in values:
            if v.age > start and v.age <= end:
              reduced_v.append(v)
            elif v.age == 0 and end == right_end:
              v.age = rdfvalue.RDFDatetime(end)
              reduced_v.append(v)
          clone_attrs.setdefault(k, []).extend(reduced_v)
  
        cur_type = clone_attrs[aff4object.Schema.TYPE][0].ToRDFValue()
        obj_cls = AFF4Object.classes[str(cur_type)]
        new_obj = obj_cls(
            urn,
            mode=mode,
            parent=aff4object.parent,
            clone=clone_attrs,
            token=token,
            age=(start, end),
            local_cache=local_cache,
            follow_symlinks=follow_symlinks)
        new_obj.Initialize()  # This is required to set local attributes.
        yield new_obj