include dsc in message
Sigh, dsc messages may not include their own checksums (for obvious tail-eating reasons) but said checksums are needed for including in apt source repos, so calculate them on the fly. Also rename the `files` property to `source_files` so that it does not collide with the Files: header in the dsc message. Update README appropriately
This commit is contained in:
parent
3ab5300187
commit
940ce3c07d
70
README.md
70
README.md
|
@ -147,8 +147,30 @@ the [pip](https://packaging.python.org/installing/) tool:
|
||||||
|
|
||||||
>>> from pydpkg import Dsc
|
>>> from pydpkg import Dsc
|
||||||
>>> dsc = Dsc('testdeb_0.0.0.dsc')
|
>>> dsc = Dsc('testdeb_0.0.0.dsc')
|
||||||
|
>>> dsc.standards_version
|
||||||
|
'3.9.6'
|
||||||
|
>>> dsc.format
|
||||||
|
'3.0 (quilt)'
|
||||||
|
>>> x.build_depends
|
||||||
|
'python (>= 2.6.6-3), debhelper (>= 9)'
|
||||||
|
|
||||||
|
#### Get the full set of dsc headers as a dictionary
|
||||||
|
|
||||||
>>> dsc.headers
|
>>> dsc.headers
|
||||||
{'Uploaders': 'Nathan J. Mehl <n@climate.com>', 'Binary': 'testdeb', 'Maintainer': 'Nathan J. Mehl <n@climate.com>', 'Format': '3.0 (quilt)', 'Build-Depends': 'python (>= 2.6.6-3), debhelper (>= 9)', 'Source': 'testdeb', 'Version': '0.0.0-1', 'Standards-Version': '3.9.6', 'Architecture': 'all', 'Files': ' 142ca7334ed1f70302b4504566e0c233 280 testdeb_0.0.0.orig.tar.gz\n fc80e6e7f1c1a08b78a674aaee6c1548 232 testdeb_0.0.0-1.debian.tar.xz', 'Checksums-Sha1': ' f250ac0a426b31df24fc2c98050f4fab90e456cd 280 testdeb_0.0.0.orig.tar.gz\n cb3474ff94053018957ebcf1d8a2b45f75dda449 232 testdeb_0.0.0-1.debian.tar.xz', 'Package-List': 'testdeb', 'Homepage': 'https://github.com/TheClimateCorporation', 'Checksums-Sha256': ' aa57ba8f29840383f5a96c5c8f166a9e6da7a484151938643ce2618e82bfeea7 280 testdeb_0.0.0.orig.tar.gz\n 1ddb2a7336a99bc1d203f3ddb59f6fa2d298e90cb3e59cccbe0c84e359979858 232 testdeb_0.0.0-1.debian.tar.xz'}
|
{'Architecture': 'all',
|
||||||
|
'Binary': 'testdeb',
|
||||||
|
'Build-Depends': 'python (>= 2.6.6-3), debhelper (>= 9)',
|
||||||
|
'Checksums-Sha1': ' f250ac0a426b31df24fc2c98050f4fab90e456cd 280 testdeb_0.0.0.orig.tar.gz\n cb3474ff94053018957ebcf1d8a2b45f75dda449 232 testdeb_0.0.0-1.debian.tar.xz\n 80cd7b01014a269d445c63b037b885d6002cf533 841 testdeb_0.0.0.dsc',
|
||||||
|
'Checksums-Sha256': ' aa57ba8f29840383f5a96c5c8f166a9e6da7a484151938643ce2618e82bfeea7 280 testdeb_0.0.0.orig.tar.gz\n 1ddb2a7336a99bc1d203f3ddb59f6fa2d298e90cb3e59cccbe0c84e359979858 232 testdeb_0.0.0-1.debian.tar.xz\n b5ad1591349eb48db65e6865be506ad7dbd21931902a71addee5b1db9ae1ac2a 841 testdeb_0.0.0.dsc',
|
||||||
|
'Files': ' 142ca7334ed1f70302b4504566e0c233 280 testdeb_0.0.0.orig.tar.gz\n fc80e6e7f1c1a08b78a674aaee6c1548 232 testdeb_0.0.0-1.debian.tar.xz\n 893d13a2ef13f7409c9521e8ab1dbccb 841 testdeb_0.0.0.dsc',
|
||||||
|
'Format': '3.0 (quilt)',
|
||||||
|
'Homepage': 'https://github.com/TheClimateCorporation',
|
||||||
|
'Maintainer': 'Nathan J. Mehl <n@climate.com>',
|
||||||
|
'Package-List': 'testdeb',
|
||||||
|
'Source': 'testdeb',
|
||||||
|
'Standards-Version': '3.9.6',
|
||||||
|
'Uploaders': 'Nathan J. Mehl <n@climate.com>',
|
||||||
|
'Version': '0.0.0-1'}
|
||||||
|
|
||||||
#### Interact directly with the dsc message
|
#### Interact directly with the dsc message
|
||||||
|
|
||||||
|
@ -161,7 +183,7 @@ the [pip](https://packaging.python.org/installing/) tool:
|
||||||
|
|
||||||
#### Render the dsc message as a string
|
#### Render the dsc message as a string
|
||||||
|
|
||||||
>>> print(dsc.message_str)
|
>>> print(dsc)
|
||||||
Format: 3.0 (quilt)
|
Format: 3.0 (quilt)
|
||||||
Source: testdeb
|
Source: testdeb
|
||||||
Binary: testdeb
|
Binary: testdeb
|
||||||
|
@ -173,20 +195,25 @@ the [pip](https://packaging.python.org/installing/) tool:
|
||||||
Standards-Version: 3.9.6
|
Standards-Version: 3.9.6
|
||||||
Build-Depends: python (>= 2.6.6-3), debhelper (>= 9)
|
Build-Depends: python (>= 2.6.6-3), debhelper (>= 9)
|
||||||
Package-List: testdeb
|
Package-List: testdeb
|
||||||
Checksums-Sha1: f250ac0a426b31df24fc2c98050f4fab90e456cd 280
|
Checksums-Sha1:
|
||||||
testdeb_0.0.0.orig.tar.gz
|
f250ac0a426b31df24fc2c98050f4fab90e456cd 280 testdeb_0.0.0.orig.tar.gz
|
||||||
cb3474ff94053018957ebcf1d8a2b45f75dda449 232 testdeb_0.0.0-1.debian.tar.xz
|
cb3474ff94053018957ebcf1d8a2b45f75dda449 232 testdeb_0.0.0-1.debian.tar.xz
|
||||||
Checksums-Sha256: aa57ba8f29840383f5a96c5c8f166a9e6da7a484151938643ce2618e82bfeea7 280
|
80cd7b01014a269d445c63b037b885d6002cf533 841 testdeb_0.0.0.dsc
|
||||||
testdeb_0.0.0.orig.tar.gz
|
Checksums-Sha256:
|
||||||
1ddb2a7336a99bc1d203f3ddb59f6fa2d298e90cb3e59cccbe0c84e359979858 232
|
aa57ba8f29840383f5a96c5c8f166a9e6da7a484151938643ce2618e82bfeea7 280 testdeb_0.0.0.orig.tar.gz
|
||||||
testdeb_0.0.0-1.debian.tar.xz
|
1ddb2a7336a99bc1d203f3ddb59f6fa2d298e90cb3e59cccbe0c84e359979858 232 testdeb_0.0.0-1.debian.tar.xz
|
||||||
Files: 142ca7334ed1f70302b4504566e0c233 280 testdeb_0.0.0.orig.tar.gz
|
b5ad1591349eb48db65e6865be506ad7dbd21931902a71addee5b1db9ae1ac2a 841 testdeb_0.0.0.dsc
|
||||||
|
Files:
|
||||||
|
142ca7334ed1f70302b4504566e0c233 280 testdeb_0.0.0.orig.tar.gz
|
||||||
fc80e6e7f1c1a08b78a674aaee6c1548 232 testdeb_0.0.0-1.debian.tar.xz
|
fc80e6e7f1c1a08b78a674aaee6c1548 232 testdeb_0.0.0-1.debian.tar.xz
|
||||||
|
893d13a2ef13f7409c9521e8ab1dbccb 841 testdeb_0.0.0.dsc
|
||||||
|
|
||||||
#### List the package source files from the dsc
|
#### List the package source files from the dsc
|
||||||
|
|
||||||
>>> dsc.files
|
>>> dsc.source_files
|
||||||
['/home/n/testdeb_0.0.0.orig.tar.gz', 'home/n/testdeb_0.0.0-1.debian.tar.xz']
|
['/tmp/testdeb_0.0.0.orig.tar.gz',
|
||||||
|
'/tmp/testdeb_0.0.0-1.debian.tar.xz',
|
||||||
|
'/tmp/testdeb_0.0.0.dsc' ]
|
||||||
|
|
||||||
#### Validate that the package source files are present
|
#### Validate that the package source files are present
|
||||||
|
|
||||||
|
@ -199,19 +226,24 @@ the [pip](https://packaging.python.org/installing/) tool:
|
||||||
|
|
||||||
>>> bad = Dsc('testdeb_1.1.1-bad.dsc')
|
>>> bad = Dsc('testdeb_1.1.1-bad.dsc')
|
||||||
>>> bad.missing_files
|
>>> bad.missing_files
|
||||||
['/home/n/testdeb_1.1.1.orig.tar.gz', '/home/n/testdeb_1.1.1-1.debian.tar.xz']
|
['/tmp/testdeb_1.1.1.orig.tar.gz', '/tmp/testdeb_1.1.1-1.debian.tar.xz']
|
||||||
>>> bad.all_files_present
|
>>> bad.all_files_present
|
||||||
False
|
False
|
||||||
>>> bad.validate()
|
>>> bad.validate()
|
||||||
pydpkg.DscMissingFileError: ['/home/n/testdeb_1.1.1.orig.tar.gz', '/home/n/testdeb_1.1.1-1.debian.tar.xz']
|
pydpkg.DscMissingFileError: ['/tmp/testdeb_1.1.1.orig.tar.gz', '/tmp/testdeb_1.1.1-1.debian.tar.xz']
|
||||||
|
|
||||||
#### Inspect the source file checksums from the dsc
|
#### Inspect the source file checksums from the dsc
|
||||||
|
|
||||||
>>> pp(dsc.checksums)
|
>>> pp(dsc.checksums)
|
||||||
{'sha1': {'/home/n/testdeb_0.0.0-1.debian.tar.xz': 'cb3474ff94053018957ebcf1d8a2b45f75dda449',
|
{'md5': {'/tmp/testdeb_0.0.0-1.debian.tar.xz': 'fc80e6e7f1c1a08b78a674aaee6c1548',
|
||||||
'/home/n/testdeb_0.0.0.orig.tar.gz': 'f250ac0a426b31df24fc2c98050f4fab90e456cd'},
|
'/tmp/testdeb_0.0.0.dsc': '893d13a2ef13f7409c9521e8ab1dbccb',
|
||||||
'sha256': {'/home/n/testdeb_0.0.0-1.debian.tar.xz': '1ddb2a7336a99bc1d203f3ddb59f6fa2d298e90cb3e59cccbe0c84e359979858',
|
'/tmp/testdeb_0.0.0.orig.tar.gz': '142ca7334ed1f70302b4504566e0c233'},
|
||||||
'/home/n/testdeb_0.0.0.orig.tar.gz': 'aa57ba8f29840383f5a96c5c8f166a9e6da7a484151938643ce2618e82bfeea7'}}
|
'sha1': {'/tmp/testdeb_0.0.0-1.debian.tar.xz': 'cb3474ff94053018957ebcf1d8a2b45f75dda449',
|
||||||
|
'/tmp/testdeb_0.0.0.dsc': '80cd7b01014a269d445c63b037b885d6002cf533',
|
||||||
|
'/tmp/testdeb_0.0.0.orig.tar.gz': 'f250ac0a426b31df24fc2c98050f4fab90e456cd'},
|
||||||
|
'sha256': {'/tmp/testdeb_0.0.0-1.debian.tar.xz': '1ddb2a7336a99bc1d203f3ddb59f6fa2d298e90cb3e59cccbe0c84e359979858',
|
||||||
|
'/tmp/testdeb_0.0.0.dsc': 'b5ad1591349eb48db65e6865be506ad7dbd21931902a71addee5b1db9ae1ac2a',
|
||||||
|
'/tmp/testdeb_0.0.0.orig.tar.gz': 'aa57ba8f29840383f5a96c5c8f166a9e6da7a484151938643ce2618e82bfeea7'}}
|
||||||
|
|
||||||
#### Validate that all source file checksums are correct
|
#### Validate that all source file checksums are correct
|
||||||
|
|
||||||
|
@ -224,8 +256,8 @@ the [pip](https://packaging.python.org/installing/) tool:
|
||||||
|
|
||||||
>>> bad = Dsc('testdeb_0.0.0-badchecksums.dsc')
|
>>> bad = Dsc('testdeb_0.0.0-badchecksums.dsc')
|
||||||
>>> bad.corrected_checksums
|
>>> bad.corrected_checksums
|
||||||
{'sha256': defaultdict(None, {'/home/n/testdeb_0.0.0-1.debian.tar.xz': '1ddb2a7336a99bc1d203f3ddb59f6fa2d298e90cb3e59cccbe0c84e359979858', '/home/n/testdeb_0.0.0.orig.tar.gz': 'aa57ba8f29840383f5a96c5c8f166a9e6da7a484151938643ce2618e82bfeea7'}), 'sha1': defaultdict(None, {'/home/n/testdeb_0.0.0-1.debian.tar.xz': 'cb3474ff94053018957ebcf1d8a2b45f75dda449', '/home/n/testdeb_0.0.0.orig.tar.gz': 'f250ac0a426b31df24fc2c98050f4fab90e456cd'})}
|
{'sha256': defaultdict(None, {'/tmp/testdeb_0.0.0-1.debian.tar.xz': '1ddb2a7336a99bc1d203f3ddb59f6fa2d298e90cb3e59cccbe0c84e359979858', '/tmp/testdeb_0.0.0.orig.tar.gz': 'aa57ba8f29840383f5a96c5c8f166a9e6da7a484151938643ce2618e82bfeea7'}), 'sha1': defaultdict(None, {'/tmp/testdeb_0.0.0-1.debian.tar.xz': 'cb3474ff94053018957ebcf1d8a2b45f75dda449', '/tmp/testdeb_0.0.0.orig.tar.gz': 'f250ac0a426b31df24fc2c98050f4fab90e456cd'})}
|
||||||
>>> bad.all_checksums_correct
|
>>> bad.all_checksums_correct
|
||||||
False
|
False
|
||||||
>>> bad.validate()
|
>>> bad.validate()
|
||||||
pydpkg.DscBadChecksumsError: {'sha256': defaultdict(None, {'/home/n/testdeb_0.0.0-1.debian.tar.xz': '1ddb2a7336a99bc1d203f3ddb59f6fa2d298e90cb3e59cccbe0c84e359979858', '/home/n/testdeb_0.0.0.orig.tar.gz': 'aa57ba8f29840383f5a96c5c8f166a9e6da7a484151938643ce2618e82bfeea7'}), 'sha1': defaultdict(None, {'/home/n/testdeb_0.0.0-1.debian.tar.xz': 'cb3474ff94053018957ebcf1d8a2b45f75dda449', '/home/n/testdeb_0.0.0.orig.tar.gz': 'f250ac0a426b31df24fc2c98050f4fab90e456cd'})}
|
pydpkg.DscBadChecksumsError: {'sha256': defaultdict(None, {'/tmp/testdeb_0.0.0-1.debian.tar.xz': '1ddb2a7336a99bc1d203f3ddb59f6fa2d298e90cb3e59cccbe0c84e359979858', '/tmp/testdeb_0.0.0.orig.tar.gz': 'aa57ba8f29840383f5a96c5c8f166a9e6da7a484151938643ce2618e82bfeea7'}), 'sha1': defaultdict(None, {'/tmp/testdeb_0.0.0-1.debian.tar.xz': 'cb3474ff94053018957ebcf1d8a2b45f75dda449', '/tmp/testdeb_0.0.0.orig.tar.gz': 'f250ac0a426b31df24fc2c98050f4fab90e456cd'})}
|
||||||
|
|
|
@ -568,7 +568,7 @@ class Dsc(object):
|
||||||
self._dirname = os.path.dirname(self.filename)
|
self._dirname = os.path.dirname(self.filename)
|
||||||
self._log = logger or logging.getLogger(__name__)
|
self._log = logger or logging.getLogger(__name__)
|
||||||
self._message = None
|
self._message = None
|
||||||
self._files = None
|
self._source_files = None
|
||||||
self._sizes = None
|
self._sizes = None
|
||||||
self._message_str = None
|
self._message_str = None
|
||||||
self._checksums = None
|
self._checksums = None
|
||||||
|
@ -590,6 +590,9 @@ class Dsc(object):
|
||||||
:returns: string
|
:returns: string
|
||||||
:raises: AttributeError
|
:raises: AttributeError
|
||||||
"""
|
"""
|
||||||
|
self._log.debug('grabbing attr: %s', attr)
|
||||||
|
if attr in self.__dict__:
|
||||||
|
return self.__dict__[attr]
|
||||||
# handle attributes with dashes :-(
|
# handle attributes with dashes :-(
|
||||||
munged = attr.replace('_', '-')
|
munged = attr.replace('_', '-')
|
||||||
# beware: email.Message[nonexistent] returns None not KeyError
|
# beware: email.Message[nonexistent] returns None not KeyError
|
||||||
|
@ -606,6 +609,7 @@ class Dsc(object):
|
||||||
:returns: string
|
:returns: string
|
||||||
:raises: KeyError
|
:raises: KeyError
|
||||||
"""
|
"""
|
||||||
|
self._log.debug('grabbing item: %s', item)
|
||||||
try:
|
try:
|
||||||
return getattr(self, item)
|
return getattr(self, item)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
@ -624,6 +628,7 @@ class Dsc(object):
|
||||||
@property
|
@property
|
||||||
def message(self):
|
def message(self):
|
||||||
"""Return an email.Message object containing the parsed dsc file"""
|
"""Return an email.Message object containing the parsed dsc file"""
|
||||||
|
self._log.debug('accessing message property')
|
||||||
if self._message is None:
|
if self._message is None:
|
||||||
self._message = self._process_dsc_file()
|
self._message = self._process_dsc_file()
|
||||||
return self._message
|
return self._message
|
||||||
|
@ -644,18 +649,18 @@ class Dsc(object):
|
||||||
return self._pgp_message
|
return self._pgp_message
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def files(self):
|
def source_files(self):
|
||||||
"""Return a list of source files found in the dsc file"""
|
"""Return a list of source files found in the dsc file"""
|
||||||
if self._files is None:
|
if self._source_files is None:
|
||||||
self._files = self._process_source_files()
|
self._source_files = self._process_source_files()
|
||||||
return [x[0] for x in self._files]
|
return [x[0] for x in self._source_files]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def all_files_present(self):
|
def all_files_present(self):
|
||||||
"""Return true if all files listed in the dsc have been found"""
|
"""Return true if all files listed in the dsc have been found"""
|
||||||
if self._files is None:
|
if self._source_files is None:
|
||||||
self._files = self._process_source_files()
|
self._source_files = self._process_source_files()
|
||||||
return all([x[2] for x in self._files])
|
return all([x[2] for x in self._source_files])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def all_checksums_correct(self):
|
def all_checksums_correct(self):
|
||||||
|
@ -673,16 +678,16 @@ class Dsc(object):
|
||||||
@property
|
@property
|
||||||
def missing_files(self):
|
def missing_files(self):
|
||||||
"""Return a list of all files from the dsc that we failed to find"""
|
"""Return a list of all files from the dsc that we failed to find"""
|
||||||
if self._files is None:
|
if self._source_files is None:
|
||||||
self._files = self._process_source_files()
|
self._source_files = self._process_source_files()
|
||||||
return [x[0] for x in self._files if x[2] is False]
|
return [x[0] for x in self._source_files if x[2] is False]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sizes(self):
|
def sizes(self):
|
||||||
"""Return a list of source files found in the dsc file"""
|
"""Return a list of source files found in the dsc file"""
|
||||||
if self._files is None:
|
if self._source_files is None:
|
||||||
self._files = self._process_source_files()
|
self._source_files = self._process_source_files()
|
||||||
return dict([(x[0], x[1]) for x in self._files])
|
return dict([(x[0], x[1]) for x in self._source_files])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def message_str(self):
|
def message_str(self):
|
||||||
|
@ -706,7 +711,7 @@ class Dsc(object):
|
||||||
"""Raise exceptions if files are missing or checksums are bad."""
|
"""Raise exceptions if files are missing or checksums are bad."""
|
||||||
if not self.all_files_present:
|
if not self.all_files_present:
|
||||||
raise DscMissingFileError(
|
raise DscMissingFileError(
|
||||||
[x[0] for x in self._files if not x[2]])
|
[x[0] for x in self._source_files if not x[2]])
|
||||||
if not self.all_checksums_correct:
|
if not self.all_checksums_correct:
|
||||||
raise DscBadChecksumsError(self.corrected_checksums)
|
raise DscBadChecksumsError(self.corrected_checksums)
|
||||||
|
|
||||||
|
@ -714,38 +719,83 @@ class Dsc(object):
|
||||||
"""Walk through the dsc message looking for any keys in the
|
"""Walk through the dsc message looking for any keys in the
|
||||||
format 'Checksum-hashtype'. Return a nested dictionary in
|
format 'Checksum-hashtype'. Return a nested dictionary in
|
||||||
the form {hashtype: {filename: {digest}}}"""
|
the form {hashtype: {filename: {digest}}}"""
|
||||||
|
self._log.debug('process_checksums()')
|
||||||
sums = {}
|
sums = {}
|
||||||
for key in self.message.keys():
|
for key in self.message.keys():
|
||||||
if key.lower().startswith('checksums'):
|
if key.lower().startswith('checksums'):
|
||||||
hashtype = key.split('-')[1].lower()
|
hashtype = key.split('-')[1].lower()
|
||||||
sums[hashtype] = {}
|
# grrrrrr debian :( :( :(
|
||||||
source = self.message[key]
|
elif key.lower() == 'files':
|
||||||
for line in source.split('\n'):
|
hashtype = 'md5'
|
||||||
if line:
|
else:
|
||||||
digest, _, filename = line.strip().split(' ')
|
continue
|
||||||
pathname = os.path.abspath(
|
sums[hashtype] = {}
|
||||||
os.path.join(self._dirname, filename))
|
source = self.message[key]
|
||||||
sums[hashtype][pathname] = digest
|
for line in source.split('\n'):
|
||||||
|
if line: # grrr py3--
|
||||||
|
digest, _, filename = line.strip().split(' ')
|
||||||
|
pathname = os.path.abspath(
|
||||||
|
os.path.join(self._dirname, filename))
|
||||||
|
sums[hashtype][pathname] = digest
|
||||||
return sums
|
return sums
|
||||||
|
|
||||||
|
def _internalize_message(self, msg):
|
||||||
|
"""Ugh: the dsc message body may not include a Files or
|
||||||
|
Checksums-foo entry for _itself_, which makes for hilarious
|
||||||
|
misadventures up the chain. So, pfeh, we add it."""
|
||||||
|
self._log.debug('internalize_message()')
|
||||||
|
base = os.path.basename(self.filename)
|
||||||
|
size = os.path.getsize(self.filename)
|
||||||
|
for key, source in msg.items():
|
||||||
|
self._log.debug('processing key: %s', key)
|
||||||
|
if key.lower().startswith('checksums'):
|
||||||
|
hashtype = key.split('-')[1].lower()
|
||||||
|
elif key.lower() == 'files':
|
||||||
|
hashtype = 'md5'
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
found = []
|
||||||
|
for line in source.split('\n'):
|
||||||
|
if line: # grrr
|
||||||
|
found.append(line.strip().split(' '))
|
||||||
|
files = [x[2] for x in found]
|
||||||
|
if base not in files:
|
||||||
|
self._log.debug('dsc file not found in %s: %s', key, base)
|
||||||
|
self._log.debug('getting hasher for %s', hashtype)
|
||||||
|
hasher = getattr(hashlib, hashtype)()
|
||||||
|
self._log.debug('hashing file')
|
||||||
|
with open(self.filename, 'rb') as fileobj:
|
||||||
|
# pylint: disable=cell-var-from-loop
|
||||||
|
for chunk in iter(lambda: fileobj.read(1024), b''):
|
||||||
|
hasher.update(chunk)
|
||||||
|
self._log.debug('completed hashing file')
|
||||||
|
self._log.debug('got %s digest: %s',
|
||||||
|
hashtype, hasher.hexdigest())
|
||||||
|
newline = '\n {0} {1} {2}'.format(
|
||||||
|
hasher.hexdigest(), size, base)
|
||||||
|
self._log.debug('new line: %s', newline)
|
||||||
|
msg.replace_header(key, msg[key] + newline)
|
||||||
|
return msg
|
||||||
|
|
||||||
def _process_dsc_file(self):
|
def _process_dsc_file(self):
|
||||||
"""Extract the dsc message from a file: parse the dsc body
|
"""Extract the dsc message from a file: parse the dsc body
|
||||||
and return an email.Message object. Attempt to extract the
|
and return an email.Message object. Attempt to extract the
|
||||||
RFC822 message from an OpenPGP message if necessary."""
|
RFC822 message from an OpenPGP message if necessary."""
|
||||||
|
self._log.debug('process_dsc_file()')
|
||||||
if not self.filename.endswith('.dsc'):
|
if not self.filename.endswith('.dsc'):
|
||||||
self._log.warning(
|
self._log.debug(
|
||||||
'File %s does not appear to be a dsc file; pressing '
|
'File %s does not appear to be a dsc file; pressing '
|
||||||
'on but we may experience some turbulence and possibly '
|
'on but we may experience some turbulence and possibly '
|
||||||
'explode.', self.filename)
|
'explode.', self.filename)
|
||||||
try:
|
try:
|
||||||
self._pgp_message = pgpy.PGPMessage.from_file(self.filename)
|
self._pgp_message = pgpy.PGPMessage.from_file(self.filename)
|
||||||
|
self._log.debug('Found pgp signed message')
|
||||||
msg = message_from_string(self._pgp_message.message)
|
msg = message_from_string(self._pgp_message.message)
|
||||||
except TypeError as ex:
|
except TypeError as ex:
|
||||||
self._log.exception(ex)
|
self._log.exception(ex)
|
||||||
self._log.fatal(
|
self._log.fatal(
|
||||||
'dsc file %s has a corrupt signature: %s', self.filename, ex)
|
'dsc file %s has a corrupt signature: %s', self.filename, ex)
|
||||||
raise DscBadSignatureError
|
raise DscBadSignatureError
|
||||||
# '%s has a corrupt signature' % self.filename)
|
|
||||||
except IOError as ex:
|
except IOError as ex:
|
||||||
self._log.fatal('Could not read dsc file "%s": %s',
|
self._log.fatal('Could not read dsc file "%s": %s',
|
||||||
self.filename, ex)
|
self.filename, ex)
|
||||||
|
@ -755,6 +805,7 @@ class Dsc(object):
|
||||||
self.filename, ex)
|
self.filename, ex)
|
||||||
with open(self.filename) as fileobj:
|
with open(self.filename) as fileobj:
|
||||||
msg = message_from_file(fileobj)
|
msg = message_from_file(fileobj)
|
||||||
|
msg = self._internalize_message(msg)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def _process_source_files(self):
|
def _process_source_files(self):
|
||||||
|
@ -768,6 +819,7 @@ class Dsc(object):
|
||||||
Also extract the file size from the message lines and fill
|
Also extract the file size from the message lines and fill
|
||||||
out the _files dictionary.
|
out the _files dictionary.
|
||||||
"""
|
"""
|
||||||
|
self._log.debug('process_source_files()')
|
||||||
filenames = []
|
filenames = []
|
||||||
try:
|
try:
|
||||||
files = self.message['Files']
|
files = self.message['Files']
|
||||||
|
@ -789,6 +841,7 @@ class Dsc(object):
|
||||||
dsc file. Check each in turn. If any checksum is invalid,
|
dsc file. Check each in turn. If any checksum is invalid,
|
||||||
append the correct checksum to a similarly structured dict
|
append the correct checksum to a similarly structured dict
|
||||||
and return them all at the end."""
|
and return them all at the end."""
|
||||||
|
self._log.debug('validate_checksums()')
|
||||||
bad_hashes = defaultdict(lambda: defaultdict(None))
|
bad_hashes = defaultdict(lambda: defaultdict(None))
|
||||||
for hashtype, filenames in six.iteritems(self.checksums):
|
for hashtype, filenames in six.iteritems(self.checksums):
|
||||||
for filename, digest in six.iteritems(filenames):
|
for filename, digest in six.iteritems(filenames):
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -1,6 +1,6 @@
|
||||||
from distutils.core import setup
|
from distutils.core import setup
|
||||||
|
|
||||||
__VERSION__ = '1.3.0'
|
__VERSION__ = '1.3.1'
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='pydpkg',
|
name='pydpkg',
|
||||||
|
|
|
@ -5,7 +5,11 @@ import unittest
|
||||||
import pytest
|
import pytest
|
||||||
from email.message import Message
|
from email.message import Message
|
||||||
|
|
||||||
from pydpkg import Dsc, DscMissingFileError, DscBadSignatureError, DscBadChecksumsError
|
from pydpkg import Dsc
|
||||||
|
from pydpkg import DscMissingFileError
|
||||||
|
from pydpkg import DscBadSignatureError
|
||||||
|
from pydpkg import DscBadChecksumsError
|
||||||
|
|
||||||
from pgpy import PGPMessage
|
from pgpy import PGPMessage
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,12 +21,14 @@ TEST_BAD_CHECKSUMS_FILE = 'testdeb_0.0.0-badchecksums.dsc'
|
||||||
|
|
||||||
|
|
||||||
class DscTest(unittest.TestCase):
|
class DscTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
goodfile = os.path.join(os.path.dirname(__file__), TEST_DSC_FILE)
|
self.dirn = os.path.dirname(__file__)
|
||||||
signed = os.path.join(os.path.dirname(__file__), TEST_SIGNED_DSC_FILE)
|
goodfile = os.path.join(self.dirn, TEST_DSC_FILE)
|
||||||
badfile = os.path.join(os.path.dirname(__file__), TEST_BAD_DSC_FILE)
|
signed = os.path.join(self.dirn, TEST_SIGNED_DSC_FILE)
|
||||||
badsigned = os.path.join(os.path.dirname(__file__), TEST_BAD_SIGNED_FILE)
|
badfile = os.path.join(self.dirn, TEST_BAD_DSC_FILE)
|
||||||
badchecksums = os.path.join(os.path.dirname(__file__), TEST_BAD_CHECKSUMS_FILE)
|
badsigned = os.path.join(self.dirn, TEST_BAD_SIGNED_FILE)
|
||||||
|
badchecksums = os.path.join(self.dirn, TEST_BAD_CHECKSUMS_FILE)
|
||||||
self.good = Dsc(goodfile)
|
self.good = Dsc(goodfile)
|
||||||
self.signed = Dsc(signed)
|
self.signed = Dsc(signed)
|
||||||
self.bad = Dsc(badfile)
|
self.bad = Dsc(badfile)
|
||||||
|
@ -50,23 +56,19 @@ class DscTest(unittest.TestCase):
|
||||||
def test_message(self):
|
def test_message(self):
|
||||||
self.assertIsInstance(self.good.message, type(Message()))
|
self.assertIsInstance(self.good.message, type(Message()))
|
||||||
|
|
||||||
def test_found_files(self):
|
def test_found_source_files(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.good.files,
|
self.good.source_files,
|
||||||
[os.path.join(os.path.dirname(__file__),
|
[os.path.join(self.dirn, 'testdeb_0.0.0.orig.tar.gz'),
|
||||||
'testdeb_0.0.0.orig.tar.gz'),
|
os.path.join(self.dirn, 'testdeb_0.0.0-1.debian.tar.xz'),
|
||||||
os.path.join(os.path.dirname(__file__),
|
os.path.join(self.dirn, 'testdeb_0.0.0.dsc')])
|
||||||
'testdeb_0.0.0-1.debian.tar.xz')]
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_missing_files(self):
|
def test_missing_files(self):
|
||||||
self.assertEqual(True, self.good.all_files_present)
|
self.assertEqual(True, self.good.all_files_present)
|
||||||
self.assertEqual(False, self.bad.all_files_present)
|
self.assertEqual(False, self.bad.all_files_present)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
[os.path.join(os.path.dirname(__file__),
|
[os.path.join(self.dirn, 'testdeb_1.1.1.orig.tar.gz'),
|
||||||
'testdeb_1.1.1.orig.tar.gz'),
|
os.path.join(self.dirn, 'testdeb_1.1.1-1.debian.tar.xz')],
|
||||||
os.path.join(os.path.dirname(__file__),
|
|
||||||
'testdeb_1.1.1-1.debian.tar.xz')],
|
|
||||||
self.bad.missing_files)
|
self.bad.missing_files)
|
||||||
with pytest.raises(DscMissingFileError):
|
with pytest.raises(DscMissingFileError):
|
||||||
self.bad.validate()
|
self.bad.validate()
|
||||||
|
@ -75,15 +77,58 @@ class DscTest(unittest.TestCase):
|
||||||
self.assertEqual(None, self.good.pgp_message)
|
self.assertEqual(None, self.good.pgp_message)
|
||||||
self.assertEqual(self.signed.source, 'testdeb')
|
self.assertEqual(self.signed.source, 'testdeb')
|
||||||
with pytest.raises(DscBadSignatureError):
|
with pytest.raises(DscBadSignatureError):
|
||||||
self.badsigned.files
|
self.badsigned.source_files
|
||||||
self.assertIsInstance(self.signed.pgp_message, PGPMessage)
|
self.assertIsInstance(self.signed.pgp_message, PGPMessage)
|
||||||
|
|
||||||
|
def test_parse_checksums(self):
|
||||||
|
xz = os.path.join(self.dirn, 'testdeb_0.0.0-1.debian.tar.xz')
|
||||||
|
gz = os.path.join(self.dirn, 'testdeb_0.0.0.orig.tar.gz')
|
||||||
|
dsc = os.path.join(self.dirn, 'testdeb_0.0.0.dsc')
|
||||||
|
self.assertEqual(
|
||||||
|
self.good.checksums,
|
||||||
|
{'md5': {xz: 'fc80e6e7f1c1a08b78a674aaee6c1548',
|
||||||
|
dsc: '893d13a2ef13f7409c9521e8ab1dbccb',
|
||||||
|
gz: '142ca7334ed1f70302b4504566e0c233'},
|
||||||
|
'sha1': {xz: 'cb3474ff94053018957ebcf1d8a2b45f75dda449',
|
||||||
|
dsc: '80cd7b01014a269d445c63b037b885d6002cf533',
|
||||||
|
gz: 'f250ac0a426b31df24fc2c98050f4fab90e456cd'},
|
||||||
|
'sha256': {
|
||||||
|
xz: '1ddb2a7336a99bc1d203f3ddb59f6fa2d298e90cb3e59cccbe0c84e359979858',
|
||||||
|
dsc: 'b5ad1591349eb48db65e6865be506ad7dbd21931902a71addee5b1db9ae1ac2a',
|
||||||
|
gz: 'aa57ba8f29840383f5a96c5c8f166a9e6da7a484151938643ce2618e82bfeea7'}})
|
||||||
|
|
||||||
def test_checksum_validation(self):
|
def test_checksum_validation(self):
|
||||||
self.assertEqual(True, self.good.all_checksums_correct)
|
self.assertEqual(True, self.good.all_checksums_correct)
|
||||||
self.assertEqual(False, self.badchecksums.all_checksums_correct)
|
self.assertEqual(False, self.badchecksums.all_checksums_correct)
|
||||||
with pytest.raises(DscBadChecksumsError):
|
with pytest.raises(DscBadChecksumsError):
|
||||||
self.badchecksums.validate()
|
self.badchecksums.validate()
|
||||||
|
|
||||||
|
def test_message_internalization(self):
|
||||||
|
self.maxDiff = None
|
||||||
|
files = """142ca7334ed1f70302b4504566e0c233 280 testdeb_0.0.0.orig.tar.gz
|
||||||
|
fc80e6e7f1c1a08b78a674aaee6c1548 232 testdeb_0.0.0-1.debian.tar.xz
|
||||||
|
893d13a2ef13f7409c9521e8ab1dbccb 841 testdeb_0.0.0.dsc"""
|
||||||
|
sha_1 = """f250ac0a426b31df24fc2c98050f4fab90e456cd 280 testdeb_0.0.0.orig.tar.gz
|
||||||
|
cb3474ff94053018957ebcf1d8a2b45f75dda449 232 testdeb_0.0.0-1.debian.tar.xz
|
||||||
|
80cd7b01014a269d445c63b037b885d6002cf533 841 testdeb_0.0.0.dsc"""
|
||||||
|
sha_256 = """aa57ba8f29840383f5a96c5c8f166a9e6da7a484151938643ce2618e82bfeea7 280 testdeb_0.0.0.orig.tar.gz
|
||||||
|
1ddb2a7336a99bc1d203f3ddb59f6fa2d298e90cb3e59cccbe0c84e359979858 232 testdeb_0.0.0-1.debian.tar.xz
|
||||||
|
b5ad1591349eb48db65e6865be506ad7dbd21931902a71addee5b1db9ae1ac2a 841 testdeb_0.0.0.dsc"""
|
||||||
|
self.assertEqual(
|
||||||
|
# gah. the py2 and py3 email.Message implementations appear to
|
||||||
|
# disagree on whether retreived multiline header strings will start
|
||||||
|
# with a newline :( :( :(
|
||||||
|
self.good.message['Files'].strip(),
|
||||||
|
files)
|
||||||
|
self.assertEqual(
|
||||||
|
# ibid.
|
||||||
|
self.good.message['checksums-sha1'].strip(),
|
||||||
|
sha_1)
|
||||||
|
self.assertEqual(
|
||||||
|
# op. cit.
|
||||||
|
self.good.message['checksums-sha256'].strip(),
|
||||||
|
sha_256)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
suite = unittest.TestLoader().loadTestsFromTestCase(DscTest)
|
suite = unittest.TestLoader().loadTestsFromTestCase(DscTest)
|
||||||
|
|
Loading…
Reference in New Issue