def filter_format(self, format, regex=False):
        """
        Generator including all archive entries with a given format.
        Is regex=True, format will be compiled to a regular expression and
        matched against the entry formats
        """
        if not format:
            raise KeyError('You need to provide an format')

        if regex is True:
            pattern = re.compile(format)
        else:
            pattern = None
            # check format argument against spec
            try:
                utils.check_format(format)
            except exceptions.CombineArchiveFormatException as e:
                raise KeyError(
                    '{format} is no valid format, according to the OMEX specification. {cause}'
                    .format(format=format, cause=e.message))

        for (location, entry) in self.entries.items():
            if pattern is not None and pattern.match(entry.format):
                yield entry
            elif pattern is None and format == entry.format:
                yield entry
    def filter_format(self, format, regex=False):
        """
        Generator including all archive entries with a given format.
        Is regex=True, format will be compiled to a regular expression and
        matched against the entry formats
        """
        if not format:
            raise KeyError('You need to provide an format')

        if regex is True:
            pattern = re.compile(format)
        else:
            pattern = None
            # check format argument against spec
            try:
                utils.check_format(format)
            except exceptions.CombineArchiveFormatException as e:
                raise KeyError(
                    '{format} is no valid format, according to the OMEX specification. {cause}'.format(format=format,
                                                                                                       cause=e.message))

        for (location, entry) in self.entries.items():
            if pattern is not None and pattern.match(entry.format):
                yield entry
            elif pattern is None and format == entry.format:
                yield entry
    def _read_manifest(self):
        """
        internal function.
        Reads the manifest file of a COMBINE Archive
        """
        try:
            with self._zip.open(self.MANIFEST_LOCATION) as manifest_file:
                manifest = ElementTree.fromstring(manifest_file.read())
        except KeyError:
            # manifest does not exists, probably an empty/new archive
            return False
        except ElementTree.ParseError as e:
            raise exceptions.CombineArchiveException(
                'Cannot parse xml manifest. {}'.format(e.msg))

        # check for correct root element and namespace
        if manifest.tag != utils.extend_tag_name(_XML_ROOT_ELEM, _XML_NS):
            raise exceptions.CombineArchiveException(
                'manifest has no valid omex root element')

        # check entries
        for entry in manifest.findall(_XML_CONTENT_TAG, _XML_NS):
            try:
                location = utils.get_attribute(entry, _XML_CONTENT_LOCATION,
                                               _XML_NS)
                entry_format = utils.check_format(utils.get_attribute(
                    entry, _XML_CONTENT_FORMAT, _XML_NS),
                                                  convert=False)
                master = True if entry.attrib.get(_XML_CONTENT_MASTER,
                                                  False) in ('True', 'true',
                                                             True) else False
            except KeyError:
                raise exceptions.CombineArchiveException(
                    'location and format field are required. Corrupt manifest.xml'
                )

            # clean location
            location = utils.clean_pathname(location)

            # check if file is in zip, if it's not the root element
            zipinfo = None
            if location not in self.ARCHIVE_REFERENCE:
                try:
                    zipinfo = self._zip.getinfo(location)
                except KeyError:
                    raise exceptions.CombineArchiveException(
                        '{location} is specified by the manifest, but not contained by the ZIP file'
                        .format(location=location))

            archive_entry = ArchiveEntry(location,
                                         format=entry_format,
                                         master=master,
                                         archive=self,
                                         zipinfo=zipinfo)
            self.entries[location] = archive_entry
    def add_entry(self,
                  file,
                  format,
                  location=None,
                  master=False,
                  replace=False):
        """
        adds a file-like object to the COMBINE archive and adds a manifest entry
        if file is an instance of unicode or str, the content of this variable is written as content

        Returns:
            ArchiveEntry
        """
        if not file or not format:
            raise exceptions.CombineArchiveException(
                'both a file and the corresponding format must be provided')
        # check format schema
        format = utils.check_format(format)

        # no location provided. Guess it
        if location is None or not location:
            location = os.path.basename(file)

        # clean location
        location = utils.clean_pathname(location)

        if location == self.MANIFEST_LOCATION or location in self.ARCHIVE_REFERENCE:
            raise exceptions.CombineArchiveException(
                'it is not allowed to name a file {loc}'.format(loc=location))

        if location in self._zip.namelist():
            if replace is False:
                raise exceptions.CombineArchiveException(
                    '{loc} exists already in the COMBINE archive. set replace=True, to override it'
                    .format(loc=location))
            else:
                self.remove_entry(location)

        # write file to zip
        if isinstance(file, (str, unicode)):
            # file is actually string
            zipinfo = self._zip.writestr(location, file)
        else:
            zipinfo = self._zip.write(file, location)

        entry = ArchiveEntry(location,
                             format=format,
                             master=master,
                             zipinfo=zipinfo,
                             archive=self)
        self.entries[entry.location] = entry
        return entry
    def _write_manifest(self, zip_file=None):
        """
        internal function.
        Writes the manifest file of a COMBINE Archive
        """
        if zip_file is None:
            zip_file = self._zip

        # create new DOM object
        manifest = ElementTree.Element(
            utils.extend_tag_name(_XML_ROOT_ELEM, _XML_NS))

        # write first entry for archive itself
        content = ElementTree.SubElement(
            manifest, utils.extend_tag_name(_XML_CONTENT_TAG, _XML_NS))
        content.attrib.update({
            utils.extend_tag_name(_XML_CONTENT_LOCATION, _XML_NS):
            '.',
            utils.extend_tag_name(_XML_CONTENT_FORMAT, _XML_NS):
            _XML_CONTENT_ARCHIVE_TYPE,
        })

        for (location, entry) in self.entries.items():
            entry_format = utils.check_format(entry.format)
            content = ElementTree.SubElement(
                manifest, utils.extend_tag_name(_XML_CONTENT_TAG, _XML_NS))
            content.attrib.update({
                utils.extend_tag_name(_XML_CONTENT_LOCATION, _XML_NS):
                location,
                utils.extend_tag_name(_XML_CONTENT_FORMAT, _XML_NS):
                entry_format,
            })
            if entry.master:
                content.attrib[utils.extend_tag_name(_XML_CONTENT_MASTER,
                                                     _XML_NS)] = 'true'

        # write xml to zip
        io = StringIO()
        ElementTree.ElementTree(manifest).write(io,
                                                xml_declaration=True,
                                                default_namespace=_XML_ROOT_NS)
        try:
            zip_file.remove(self.MANIFEST_LOCATION)
        except KeyError:
            pass  # Manifest does not exist yet, so removing it will fail
        zip_file.writestr(self.MANIFEST_LOCATION, io.getvalue())
        io.close()
    def _read_manifest(self):
        """
        internal function.
        Reads the manifest file of a COMBINE Archive
        """
        try:
            with self._zip.open(self.MANIFEST_LOCATION) as manifest_file:
                manifest = ElementTree.fromstring(manifest_file.read())
        except KeyError:
            # manifest does not exists, probably an empty/new archive
            return False
        except ElementTree.ParseError as e:
            raise exceptions.CombineArchiveException('Cannot parse xml manifest. {}'.format(e.msg))

        # check for correct root element and namespace
        if manifest.tag != utils.extend_tag_name(_XML_ROOT_ELEM, _XML_NS):
            raise exceptions.CombineArchiveException('manifest has no valid omex root element')

        # check entries
        for entry in manifest.findall(_XML_CONTENT_TAG, _XML_NS):
            try:
                location = utils.get_attribute(entry, _XML_CONTENT_LOCATION, _XML_NS)
                entry_format = utils.check_format(utils.get_attribute(entry, _XML_CONTENT_FORMAT, _XML_NS), convert=False)
                master = True if entry.attrib.get(_XML_CONTENT_MASTER, False) in ('True', 'true', True) else False
            except KeyError:
                raise exceptions.CombineArchiveException('location and format field are required. Corrupt manifest.xml')

            # clean location
            location = utils.clean_pathname(location)

            # check if file is in zip, if it's not the root element
            zipinfo = None
            if location not in self.ARCHIVE_REFERENCE:
                try:
                    zipinfo = self._zip.getinfo(location)
                except KeyError:
                    raise exceptions.CombineArchiveException(
                        '{location} is specified by the manifest, but not contained by the ZIP file'.format(location=location))

            archive_entry = ArchiveEntry(location, format=entry_format, master=master, archive=self, zipinfo=zipinfo)
            self.entries[location] = archive_entry
    def add_entry(self, file, format, location=None, master=False, replace=False):
        """
        adds a file-like object to the COMBINE archive and adds a manifest entry
        if file is an instance of unicode or str, the content of this variable is written as content

        Returns:
            ArchiveEntry
        """
        if not file or not format:
            raise exceptions.CombineArchiveException('both a file and the corresponding format must be provided')
        # check format schema
        format = utils.check_format(format)

        # no location provided. Guess it
        if location is None or not location:
            location = os.path.basename(file)

        # clean location
        location = utils.clean_pathname(location)

        if location == self.MANIFEST_LOCATION or location in self.ARCHIVE_REFERENCE:
            raise exceptions.CombineArchiveException('it is not allowed to name a file {loc}'.format(loc=location))

        if location in self._zip.namelist():
            if replace is False:
                raise exceptions.CombineArchiveException('{loc} exists already in the COMBINE archive. set replace=True, to override it'.format(loc=location))
            else:
                self.remove_entry(location)

        # write file to zip
        if isinstance(file, (str, unicode)):
            # file is actually string
            zipinfo = self._zip.writestr(location, file)
        else:
            zipinfo = self._zip.write(file, location)

        entry = ArchiveEntry(location, format=format, master=master, zipinfo=zipinfo, archive=self)
        self.entries[entry.location] = entry
        return entry
    def _write_manifest(self, zip_file=None):
        """
        internal function.
        Writes the manifest file of a COMBINE Archive
        """
        if zip_file is None:
            zip_file = self._zip

        # create new DOM object
        manifest = ElementTree.Element(utils.extend_tag_name(_XML_ROOT_ELEM, _XML_NS))

        # write first entry for archive itself
        content = ElementTree.SubElement(manifest, utils.extend_tag_name(_XML_CONTENT_TAG, _XML_NS))
        content.attrib.update({
            utils.extend_tag_name(_XML_CONTENT_LOCATION, _XML_NS): '.',
            utils.extend_tag_name(_XML_CONTENT_FORMAT, _XML_NS): _XML_CONTENT_ARCHIVE_TYPE,
        })

        for (location, entry) in self.entries.items():
            entry_format = utils.check_format(entry.format)
            content = ElementTree.SubElement(manifest, utils.extend_tag_name(_XML_CONTENT_TAG, _XML_NS))
            content.attrib.update({
                utils.extend_tag_name(_XML_CONTENT_LOCATION, _XML_NS): location,
                utils.extend_tag_name(_XML_CONTENT_FORMAT, _XML_NS): entry_format,
            })
            if entry.master:
                content.attrib[utils.extend_tag_name(_XML_CONTENT_MASTER, _XML_NS)] = 'true'

        # write xml to zip
        io = StringIO()
        ElementTree.ElementTree(manifest).write(io, xml_declaration=True, default_namespace=_XML_ROOT_NS)
        try:
            zip_file.remove(self.MANIFEST_LOCATION)
        except KeyError:
            pass  # Manifest does not exist yet, so removing it will fail
        zip_file.writestr(self.MANIFEST_LOCATION, io.getvalue())
        io.close()