From 7bf690b4bc2b79a8b12154ee774f80e93f6265ff Mon Sep 17 00:00:00 2001 From: Seth Vidal Date: Thu, 20 Dec 2007 02:18:23 -0500 Subject: [PATCH] Whew: this is the beginning of a big conversion of createrepo to use the yum modules, behave more like a modular program and have a proper class structure. It's not done, but it's a start. --- Makefile | 55 +- bin/Makefile | 20 +- createrepo.spec | 10 +- createrepo/Makefile | 64 ++ createrepo/__init__.py | 141 +++ readMetadata.py => createrepo/readMetadata.py | 0 createrepo/utils.py | 101 ++ createrepo/yumbased.py | 383 ++++++++ docs/Makefile | 12 +- dumpMetadata.py | 896 ------------------ genpkgmetadata.py | 189 ++-- 11 files changed, 836 insertions(+), 1035 deletions(-) create mode 100644 createrepo/Makefile create mode 100644 createrepo/__init__.py rename readMetadata.py => createrepo/readMetadata.py (100%) create mode 100644 createrepo/utils.py create mode 100644 createrepo/yumbased.py delete mode 100644 dumpMetadata.py diff --git a/Makefile b/Makefile index a5f2ffb..b57acfc 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,12 @@ -PACKAGE = createrepo -VERSION = 0.4.10 +PKGNAME = createrepo +VERSION=$(shell awk '/Version:/ { print $$2 }' ${PKGNAME}.spec) +RELEASE=$(shell awk '/Release:/ { print $$2 }' ${PKGNAME}.spec) +CVSTAG=createrepo-$(subst .,_,$(VERSION)-$(RELEASE)) +PYTHON=python +SUBDIRS = $(PKGNAME) bin docs +PYFILES = $(wildcard *.py) + + SHELL = /bin/sh top_srcdir = . srcdir = . @@ -20,9 +27,9 @@ includedir = ${prefix}/include oldincludedir = /usr/include mandir = ${prefix}/share/man -pkgdatadir = $(datadir)/$(PACKAGE) -pkglibdir = $(libdir)/$(PACKAGE) -pkgincludedir = $(includedir)/$(PACKAGE) +pkgdatadir = $(datadir)/$(PKGNAME) +pkglibdir = $(libdir)/$(PKGNAME) +pkgincludedir = $(includedir)/$(PKGNAME) top_builddir = # all dirs @@ -37,12 +44,8 @@ INSTALL_DATA = $(INSTALL) -m 644 INSTALL_MODULES = $(INSTALL) -m 755 -D RM = rm -f -SUBDIRS = bin docs - MODULES = $(srcdir)/genpkgmetadata.py \ - $(srcdir)/dumpMetadata.py \ - $(srcdir)/readMetadata.py \ - $(srcdir)/modifyrepo.py + $(srcdir)/modifyrepo.py .SUFFIXES: .py .pyc .py.pyc: @@ -51,7 +54,7 @@ MODULES = $(srcdir)/genpkgmetadata.py \ all: $(MODULES) for subdir in $(SUBDIRS) ; do \ - $(MAKE) -C $$subdir VERSION=$(VERSION) PACKAGE=$(PACKAGE) DESTDIR=$(DESTDIR); \ + $(MAKE) -C $$subdir VERSION=$(VERSION) PKGNAME=$(PKGNAME) DESTDIR=$(DESTDIR); \ done check: @@ -60,7 +63,7 @@ check: install: all installdirs $(INSTALL_MODULES) $(srcdir)/$(MODULES) $(DESTDIR)$(pkgdatadir) for subdir in $(SUBDIRS) ; do \ - $(MAKE) -C $$subdir install VERSION=$(VERSION) PACKAGE=$(PACKAGE); \ + $(MAKE) -C $$subdir install VERSION=$(VERSION) PKGNAME=$(PKGNAME); \ done installdirs: @@ -74,13 +77,13 @@ uninstall: $(RM) $(pkgdatadir)/$$module ; \ done for subdir in $(SUBDIRS) ; do \ - $(MAKE) -C $$subdir uninstall VERSION=$(VERSION) PACKAGE=$(PACKAGE); \ + $(MAKE) -C $$subdir uninstall VERSION=$(VERSION) PKGNAME=$(PKGNAME); \ done clean: $(RM) *.pyc *.pyo for subdir in $(SUBDIRS) ; do \ - $(MAKE) -C $$subdir clean VERSION=$(VERSION) PACKAGE=$(PACKAGE); \ + $(MAKE) -C $$subdir clean VERSION=$(VERSION) PKGNAME=$(PKGNAME); \ done distclean: clean @@ -88,7 +91,7 @@ distclean: clean $(RM) core $(RM) *~ for subdir in $(SUBDIRS) ; do \ - $(MAKE) -C $$subdir distclean VERSION=$(VERSION) PACKAGE=$(PACKAGE); \ + $(MAKE) -C $$subdir distclean VERSION=$(VERSION) PKGNAME=$(PKGNAME); \ done mostlyclean: @@ -102,12 +105,12 @@ maintainer-clean: dist: olddir=`pwd`; \ - distdir=$(PACKAGE)-$(VERSION); \ + distdir=$(PKGNAME)-$(VERSION); \ $(RM) -r .disttmp; \ $(INSTALL_DIR) .disttmp; \ $(INSTALL_DIR) .disttmp/$$distdir; \ $(MAKE) distfiles - distdir=$(PACKAGE)-$(VERSION); \ + distdir=$(PKGNAME)-$(VERSION); \ cd .disttmp; \ tar -cvz > ../$$distdir.tar.gz $$distdir; \ cd $$olddir @@ -115,23 +118,23 @@ dist: daily: olddir=`pwd`; \ - distdir=$(PACKAGE); \ + distdir=$(PKGNAME); \ $(RM) -r .disttmp; \ $(INSTALL_DIR) .disttmp; \ $(INSTALL_DIR) .disttmp/$$distdir; \ $(MAKE) dailyfiles day=`/bin/date +%Y%m%d`; \ - distdir=$(PACKAGE); \ + distdir=$(PKGNAME); \ tarname=$$distdir-$$day ;\ cd .disttmp; \ - perl -pi -e "s/\#DATE\#/$$day/g" $$distdir/$(PACKAGE)-daily.spec; \ + perl -pi -e "s/\#DATE\#/$$day/g" $$distdir/$(PKGNAME)-daily.spec; \ echo $$day; \ tar -cvz > ../$$tarname.tar.gz $$distdir; \ cd $$olddir $(RM) -rf .disttmp dailyfiles: - distdir=$(PACKAGE); \ + distdir=$(PKGNAME); \ cp \ $(srcdir)/*.py \ $(srcdir)/Makefile \ @@ -139,14 +142,14 @@ dailyfiles: $(srcdir)/COPYING \ $(srcdir)/COPYING.lib \ $(srcdir)/README \ - $(srcdir)/$(PACKAGE).spec \ + $(srcdir)/$(PKGNAME).spec \ $(top_srcdir)/.disttmp/$$distdir for subdir in $(SUBDIRS) ; do \ - $(MAKE) -C $$subdir dailyfiles VERSION=$(VERSION) PACKAGE=$(PACKAGE); \ + $(MAKE) -C $$subdir dailyfiles VERSION=$(VERSION) PKGNAME=$(PKGNAME); \ done distfiles: - distdir=$(PACKAGE)-$(VERSION); \ + distdir=$(PKGNAME)-$(VERSION); \ cp \ $(srcdir)/*.py \ $(srcdir)/Makefile \ @@ -154,10 +157,10 @@ distfiles: $(srcdir)/COPYING \ $(srcdir)/COPYING.lib \ $(srcdir)/README \ - $(srcdir)/$(PACKAGE).spec \ + $(srcdir)/$(PKGNAME).spec \ $(top_srcdir)/.disttmp/$$distdir for subdir in $(SUBDIRS) ; do \ - $(MAKE) -C $$subdir distfiles VERSION=$(VERSION) PACKAGE=$(PACKAGE); \ + $(MAKE) -C $$subdir distfiles VERSION=$(VERSION) PKGNAME=$(PKGNAME); \ done archive: dist diff --git a/bin/Makefile b/bin/Makefile index 52c1f50..4497230 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -18,9 +18,9 @@ includedir = ${prefix}/include oldincludedir = /usr/include mandir = ${prefix}/man -pkgdatadir = $(datadir)/$(PACKAGE) -pkglibdir = $(libdir)/$(PACKAGE) -pkgincludedir = $(includedir)/$(PACKAGE) +pkgdatadir = $(datadir)/$(PKGNAME) +pkglibdir = $(libdir)/$(PKGNAME) +pkgincludedir = $(includedir)/$(PKGNAME) top_builddir = ../ # all dirs @@ -36,16 +36,16 @@ INSTALL_MODULES = $(INSTALL) -m 755 -D RM = rm -f -all: $(srcdir)/$(PACKAGE) +all: $(srcdir)/$(PKGNAME) install: all installdirs - $(INSTALL_BIN) $(srcdir)/$(PACKAGE) $(DESTDIR)$(bindir)/$(PACKAGE) + $(INSTALL_BIN) $(srcdir)/$(PKGNAME) $(DESTDIR)$(bindir)/$(PKGNAME) $(INSTALL_BIN) $(srcdir)/modifyrepo $(DESTDIR)$(bindir)/modifyrepo uninstall: - $(RM) $(bindir)/$(PACKAGE) + $(RM) $(bindir)/$(PKGNAME) @@ -67,19 +67,19 @@ maintainer-clean: distfiles: - distdir=$(PACKAGE)-$(VERSION); \ + distdir=$(PKGNAME)-$(VERSION); \ mkdir $(top_srcdir)/.disttmp/$$distdir/bin;\ cp \ - $(srcdir)/$(PACKAGE) \ + $(srcdir)/$(PKGNAME) \ $(srcdir)/Makefile \ $(srcdir)/modifyrepo \ $(top_srcdir)/.disttmp/$$distdir/bin dailyfiles: - distdir=$(PACKAGE); \ + distdir=$(PKGNAME); \ mkdir $(top_srcdir)/.disttmp/$$distdir/bin;\ cp \ - $(srcdir)/$(PACKAGE) \ + $(srcdir)/$(PKGNAME) \ $(srcdir)/Makefile \ $(srcdir)/modifyrepo \ $(top_srcdir)/.disttmp/$$distdir/bin diff --git a/createrepo.spec b/createrepo.spec index 3c5cc75..969ad95 100644 --- a/createrepo.spec +++ b/createrepo.spec @@ -1,6 +1,8 @@ +%{!?python_sitelib: %define python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} + Summary: Creates a common metadata repository Name: createrepo -Version: 0.4.10 +Version: 0.9 Release: 1 License: GPL Group: System Environment/Base @@ -9,7 +11,7 @@ URL: http://linux.duke.edu/metadata/ BuildRoot: %{_tmppath}/%{name}-%{version}root BuildArchitectures: noarch Requires: python >= 2.1, rpm-python, rpm >= 0:4.1.1, libxml2-python -Requires: yum-metadata-parser +Requires: yum-metadata-parser, yum >= 3.2.7 %description This utility will generate a common metadata repository from a directory of @@ -35,8 +37,12 @@ rpm packages %{_bindir}/modifyrepo %{_mandir}/man8/createrepo.8* %{_mandir}/man1/modifyrepo.1* +%{python_sitelib}/createrepo %changelog +* Thu Dec 20 2007 Seth Vidal +- beginning of the new version + * Mon Dec 3 2007 Luke Macken - Add man page for modifyrepo diff --git a/createrepo/Makefile b/createrepo/Makefile new file mode 100644 index 0000000..d3d3a34 --- /dev/null +++ b/createrepo/Makefile @@ -0,0 +1,64 @@ +PYTHON=python +PACKAGE = $(shell basename `pwd`) +PYFILES = $(wildcard *.py) +PYVER := $(shell $(PYTHON) -c 'import sys; print "%.3s" %(sys.version)') +PYSYSDIR := $(shell $(PYTHON) -c 'import sys; print sys.prefix') +PYLIBDIR = $(PYSYSDIR)/lib/python$(PYVER) +PKGDIR = $(PYLIBDIR)/site-packages/$(PKGNAME) + +SHELL = /bin/sh +top_srcdir = .. +srcdir = ../$(PKGNAME) +prefix = /usr +exec_prefix = ${prefix} + +bindir = ${exec_prefix}/bin +sbindir = ${exec_prefix}/sbin +libexecdir = ${exec_prefix}/libexec +datadir = ${prefix}/share +sysconfdir = ${prefix}/etc +sharedstatedir = ${prefix}/com +localstatedir = ${prefix}/var +libdir = ${exec_prefix}/lib +infodir = ${prefix}/info +docdir = +includedir = ${prefix}/include +oldincludedir = /usr/include +mandir = ${datadir}/man + +pkgdatadir = $(datadir)/$(PKGNAME) +pkglibdir = $(libdir)/$(PKGNAME) +pkgincludedir = $(includedir)/$(PKGNAME) +top_builddir = ../ + + +all: + echo "Nothing to do" + +clean: + rm -f *.pyc *.pyo *~ + +install: + mkdir -p $(DESTDIR)/$(PKGDIR) + for p in $(PYFILES) ; do \ + install -m 644 $$p $(DESTDIR)/$(PKGDIR)/$$p; \ + done + $(PYTHON) -c "import compileall; compileall.compile_dir('$(DESTDIR)/$(PKGDIR)', 1, '$(PKGDIR)', 1)" + +distfiles: + distdir=$(PKGNAME)-$(VERSION); \ + mkdir $(top_srcdir)/.disttmp/$$distdir/$(PKGNAME);\ + cp \ + $(srcdir)/$(PYFILES) \ + $(srcdir)/Makefile \ + $(top_srcdir)/.disttmp/$$distdir/$(PKGNAME) + +dailyfiles: + distdir=$(PKGNAME); \ + mkdir $(top_srcdir)/.disttmp/$$distdir/$(PKGNAME);\ + cp \ + $(srcdir)/$(PYFILES) \ + $(srcdir)/__init__.py \ + $(srcdir)/Makefile \ + $(top_srcdir)/.disttmp/$$distdir/$(PKGNAME) + diff --git a/createrepo/__init__.py b/createrepo/__init__.py new file mode 100644 index 0000000..ac4451d --- /dev/null +++ b/createrepo/__init__.py @@ -0,0 +1,141 @@ +import exceptions +import os +import sys +import libxml2 +import hashlib +from yum import misc + +try: + import sqlitecachec +except ImportError: + pass + + +from utils import _gzipOpen, bzipFile + + +__version__ = '0.9' + + +class MDError(exceptions.Exception): + def __init__(self, value=None): + exceptions.Exception.__init__(self) + self.value = value + + def __str__(self): + return self.value + +def repoXML(node, cmds): + """generate the repomd.xml file that stores the info on the other files""" + sumtype = cmds['sumtype'] + workfiles = [(cmds['otherfile'], 'other',), + (cmds['filelistsfile'], 'filelists'), + (cmds['primaryfile'], 'primary')] + repoid='garbageid' + + repopath = os.path.join(cmds['outputdir'], cmds['tempdir']) + + if cmds['database']: + try: + dbversion = str(sqlitecachec.DBVERSION) + except AttributeError: + dbversion = '9' + rp = sqlitecachec.RepodataParserSqlite(repopath, repoid, None) + + for (file, ftype) in workfiles: + complete_path = os.path.join(repopath, file) + + zfo = _gzipOpen(complete_path) + uncsum = misc.checksum(sumtype, zfo) + zfo.close() + csum = misc.checksum(sumtype, complete_path) + timestamp = os.stat(complete_path)[8] + + db_csums = {} + db_compressed_sums = {} + + if cmds['database']: + if ftype == 'primary': + rp.getPrimary(complete_path, csum) + + elif ftype == 'filelists': + rp.getFilelists(complete_path, csum) + + elif ftype == 'other': + rp.getOtherdata(complete_path, csum) + + + tmp_result_name = '%s.xml.gz.sqlite' % ftype + tmp_result_path = os.path.join(repopath, tmp_result_name) + good_name = '%s.sqlite' % ftype + resultpath = os.path.join(repopath, good_name) + + # rename from silly name to not silly name + os.rename(tmp_result_path, resultpath) + compressed_name = '%s.bz2' % good_name + result_compressed = os.path.join(repopath, compressed_name) + db_csums[ftype] = misc.checksum(sumtype, resultpath) + + # compress the files + bzipFile(resultpath, result_compressed) + # csum the compressed file + db_compressed_sums[ftype] = misc.checksum(sumtype, result_compressed) + # remove the uncompressed file + os.unlink(resultpath) + + # timestamp the compressed file + db_timestamp = os.stat(result_compressed)[8] + + # add this data as a section to the repomdxml + db_data_type = '%s_db' % ftype + data = node.newChild(None, 'data', None) + data.newProp('type', db_data_type) + location = data.newChild(None, 'location', None) + if cmds['baseurl'] is not None: + location.newProp('xml:base', cmds['baseurl']) + + location.newProp('href', os.path.join(cmds['finaldir'], compressed_name)) + checksum = data.newChild(None, 'checksum', db_compressed_sums[ftype]) + checksum.newProp('type', sumtype) + db_tstamp = data.newChild(None, 'timestamp', str(db_timestamp)) + unchecksum = data.newChild(None, 'open-checksum', db_csums[ftype]) + unchecksum.newProp('type', sumtype) + database_version = data.newChild(None, 'database_version', dbversion) + + + data = node.newChild(None, 'data', None) + data.newProp('type', ftype) + location = data.newChild(None, 'location', None) + if cmds['baseurl'] is not None: + location.newProp('xml:base', cmds['baseurl']) + location.newProp('href', os.path.join(cmds['finaldir'], file)) + checksum = data.newChild(None, 'checksum', csum) + checksum.newProp('type', sumtype) + timestamp = data.newChild(None, 'timestamp', str(timestamp)) + unchecksum = data.newChild(None, 'open-checksum', uncsum) + unchecksum.newProp('type', sumtype) + + # if we've got a group file then checksum it once and be done + if cmds['groupfile'] is not None: + grpfile = cmds['groupfile'] + timestamp = os.stat(grpfile)[8] + sfile = os.path.basename(grpfile) + fo = open(grpfile, 'r') + output = open(os.path.join(cmds['outputdir'], cmds['tempdir'], sfile), 'w') + output.write(fo.read()) + output.close() + fo.seek(0) + csum = misc.checksum(sumtype, fo) + fo.close() + + data = node.newChild(None, 'data', None) + data.newProp('type', 'group') + location = data.newChild(None, 'location', None) + if cmds['baseurl'] is not None: + location.newProp('xml:base', cmds['baseurl']) + location.newProp('href', os.path.join(cmds['finaldir'], sfile)) + checksum = data.newChild(None, 'checksum', csum) + checksum.newProp('type', sumtype) + timestamp = data.newChild(None, 'timestamp', str(timestamp)) + + diff --git a/readMetadata.py b/createrepo/readMetadata.py similarity index 100% rename from readMetadata.py rename to createrepo/readMetadata.py diff --git a/createrepo/utils.py b/createrepo/utils.py new file mode 100644 index 0000000..bb3939c --- /dev/null +++ b/createrepo/utils.py @@ -0,0 +1,101 @@ +#!/usr/bin/python +# util functions for createrepo +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + + +import os +import sys +import bz2 +import gzip +from gzip import write32u, FNAME + +def errorprint(stuff): + print >> sys.stderr, stuff + +def _(args): + """Stub function for translation""" + return args + + +class GzipFile(gzip.GzipFile): + def _write_gzip_header(self): + self.fileobj.write('\037\213') # magic header + self.fileobj.write('\010') # compression method + fname = self.filename[:-3] + flags = 0 + if fname: + flags = FNAME + self.fileobj.write(chr(flags)) + write32u(self.fileobj, long(0)) + self.fileobj.write('\002') + self.fileobj.write('\377') + if fname: + self.fileobj.write(fname + '\000') + + +def _gzipOpen(filename, mode="rb", compresslevel=9): + return GzipFile(filename, mode, compresslevel) + +def bzipFile(source, dest): + + s_fn = open(source, 'rb') + destination = bz2.BZ2File(dest, 'w', compresslevel=9) + + while True: + data = s_fn.read(1024000) + + if not data: break + destination.write(data) + + destination.close() + s_fn.close() + + +def returnFD(filename): + try: + fdno = os.open(filename, os.O_RDONLY) + except OSError: + raise MDError, "Error opening file" + return fdno + +def utf8String(string): + """hands back a unicoded string""" + if string is None: + return '' + elif isinstance(string, unicode): + return string + try: + x = unicode(string, 'ascii') + return string + except UnicodeError: + encodings = ['utf-8', 'iso-8859-1', 'iso-8859-15', 'iso-8859-2'] + for enc in encodings: + try: + x = unicode(string, enc) + except UnicodeError: + pass + else: + if x.encode(enc) == string: + return x.encode('utf-8') + newstring = '' + for char in string: + if ord(char) > 127: + newstring = newstring + '?' + else: + newstring = newstring + char + return newstring + + diff --git a/createrepo/yumbased.py b/createrepo/yumbased.py new file mode 100644 index 0000000..ea2b9aa --- /dev/null +++ b/createrepo/yumbased.py @@ -0,0 +1,383 @@ +#!/usr/bin/python -tt + +import os +import sys +import struct +import rpm +import types +import re +import xml.sax.saxutils + +from yum.packages import YumLocalPackage +from yum.Errors import * +from yum import misc +from rpmUtils.transaction import initReadOnlyTransaction +from rpmUtils.miscutils import flagToString, stringToVersion + +fileglobs = ['.*bin\/.*', '^\/etc\/.*', '^\/usr\/lib\/sendmail$'] +file_re = [] +for glob in fileglobs: + file_re.append(re.compile(glob)) + +dirglobs = ['.*bin\/.*', '^\/etc\/.*'] +dir_re = [] +for glob in dirglobs: + dir_re.append(re.compile(glob)) + + +class CreateRepoPackage(YumLocalPackage): + def __init__(self, ts, package): + YumLocalPackage.__init__(self, ts, package) + self._checksum = None + self._stat = os.stat(package) + self.filetime = str(self._stat[-1]) + self.packagesize = str(self._stat[6]) + self._hdrstart = None + self._hdrend = None + + def _xml(self, item): + return xml.sax.saxutils.escape(item) + + def _do_checksum(self): + if not self._checksum: + self._checksum = misc.checksum('sha', self.localpath) + + return self._checksum + checksum = property(fget=lambda self: self._do_checksum()) + + def _get_header_byte_range(self): + """takes an rpm file or fileobject and returns byteranges for location of the header""" + if self._hdrstart and self._hdrend: + return (self._hdrstart, self._hdrend) + + + fo = open(self.localpath, 'r') + #read in past lead and first 8 bytes of sig header + fo.seek(104) + # 104 bytes in + binindex = fo.read(4) + # 108 bytes in + (sigindex, ) = struct.unpack('>I', binindex) + bindata = fo.read(4) + # 112 bytes in + (sigdata, ) = struct.unpack('>I', bindata) + # each index is 4 32bit segments - so each is 16 bytes + sigindexsize = sigindex * 16 + sigsize = sigdata + sigindexsize + # we have to round off to the next 8 byte boundary + disttoboundary = (sigsize % 8) + if disttoboundary != 0: + disttoboundary = 8 - disttoboundary + # 112 bytes - 96 == lead, 8 = magic and reserved, 8 == sig header data + hdrstart = 112 + sigsize + disttoboundary + + fo.seek(hdrstart) # go to the start of the header + fo.seek(8,1) # read past the magic number and reserved bytes + + binindex = fo.read(4) + (hdrindex, ) = struct.unpack('>I', binindex) + bindata = fo.read(4) + (hdrdata, ) = struct.unpack('>I', bindata) + + # each index is 4 32bit segments - so each is 16 bytes + hdrindexsize = hdrindex * 16 + # add 16 to the hdrsize to account for the 16 bytes of misc data b/t the + # end of the sig and the header. + hdrsize = hdrdata + hdrindexsize + 16 + + # header end is hdrstart + hdrsize + hdrend = hdrstart + hdrsize + fo.close() + self._hdrstart = hdrstart + self._hdrend = hdrend + + return (hdrstart, hdrend) + + hdrend = property(fget=lambda self: self._get_header_byte_range()[1]) + hdrstart = property(fget=lambda self: self._get_header_byte_range()[0]) + + def _dump_base_items(self): + msg = """ + %s + %s + + %s + %s + %s + %s + %s +