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:
Nathan J. Mehl 2017-06-23 08:40:59 -07:00
parent 3ab5300187
commit 940ce3c07d
4 changed files with 193 additions and 63 deletions

View File

@ -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'})}

View File

@ -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):

View File

@ -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',

View File

@ -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)