提交 129ee05e 编写于 作者: J Joerg Jaspert

Merge remote-tracking branch 'ansgar/pu/multiarchive-1' into merge

* ansgar/pu/multiarchive-1:
  dak/init_dirs.py: only create directories for active keyrings
  dak/init_dirs.py: do not use Dir::Pool
  change documentation style
  Python modules should not be executable
  daklib/archive.py, daklib/checks.py: implement transition blocks
  daklib/archive.py: use method to decide which policy queue to use
  daklib/archive.py, daklib/checks.py: implement upload blocks
  daklib/dbconn.py: use apt_pkg.TagSection instead of implementing our own parser
  daklib/archive.py: check for source when copying binaries
Signed-off-by: NJoerg Jaspert <joerg@debian.org>
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
......@@ -131,16 +131,9 @@ def create_directories():
process_keyring(Cnf['Dinstall::SigningPubKeyring'], secret=True)
# Process public keyrings
for keyring in session.query(Keyring).all():
for keyring in session.query(Keyring).filter_by(active=True):
process_keyring(keyring.keyring_name)
# Process pool directories
for component in session.query(Component):
directory = os.path.join( Cnf['Dir::Pool'], component.component_name )
do_dir(directory, '%s pool' % component.component_name)
# Process dists directories
# TODO: Store location of each suite in database
for suite in session.query(Suite):
......
此差异已折叠。
......@@ -20,14 +20,14 @@
"""module provided pre-acceptance tests
Please read the documentation for the `Check` class for the interface.
Please read the documentation for the L{Check} class for the interface.
"""
from daklib.config import Config
from .dbconn import *
from daklib.dbconn import *
import daklib.dbconn as dbconn
from .regexes import *
from .textutils import fix_maintainer, ParseMaintError
from daklib.regexes import *
from daklib.textutils import fix_maintainer, ParseMaintError
import daklib.lintian as lintian
import daklib.utils as utils
......@@ -48,37 +48,37 @@ class Reject(Exception):
class Check(object):
"""base class for checks
checks are called by daklib.archive.ArchiveUpload. Failing tests should
raise a `daklib.checks.Reject` exception including a human-readable
checks are called by L{daklib.archive.ArchiveUpload}. Failing tests should
raise a L{daklib.checks.Reject} exception including a human-readable
description why the upload should be rejected.
"""
def check(self, upload):
"""do checks
Args:
upload (daklib.archive.ArchiveUpload): upload to check
@type upload: L{daklib.archive.ArchiveUpload}
@param upload: upload to check
Raises:
daklib.checks.Reject
@raise daklib.checks.Reject: upload should be rejected
"""
raise NotImplemented
def per_suite_check(self, upload, suite):
"""do per-suite checks
Args:
upload (daklib.archive.ArchiveUpload): upload to check
suite (daklib.dbconn.Suite): suite to check
@type upload: L{daklib.archive.ArchiveUpload}
@param upload: upload to check
Raises:
daklib.checks.Reject
@type suite: L{daklib.dbconn.Suite}
@param suite: suite to check
@raise daklib.checks.Reject: upload should be rejected
"""
raise NotImplemented
@property
def forcable(self):
"""allow to force ignore failing test
True if it is acceptable to force ignoring a failing test,
False otherwise
C{True} if it is acceptable to force ignoring a failing test,
C{False} otherwise
"""
return False
......@@ -438,6 +438,91 @@ class ACLCheck(Check):
return True
class UploadBlockCheck(Check):
"""check for upload blocks"""
def check(self, upload):
session = upload.session
control = upload.changes.changes
source = re_field_source.match(control['Source']).group('package')
version = control['Version']
blocks = session.query(UploadBlock).filter_by(source=source) \
.filter((UploadBlock.version == version) | (UploadBlock.version == None))
for block in blocks:
if block.fingerprint == upload.fingerprint:
raise Reject('Manual upload block in place for package {0} and fingerprint {1}:\n{2}'.format(source, upload.fingerprint.fingerprint, block.reason))
if block.uid == upload.fingerprint.uid:
raise Reject('Manual upload block in place for package {0} and uid {1}:\n{2}'.format(source, block.uid.uid, block.reason))
return True
class TransitionCheck(Check):
"""check for a transition"""
def check(self, upload):
if 'source' not in upload.changes.architectures:
return True
transitions = self.get_transitions()
if transitions is None:
return True
source = re_field_source.match(control['Source']).group('package')
for trans in transitions:
t = transitions[trans]
source = t["source"]
expected = t["new"]
# Will be None if nothing is in testing.
current = get_source_in_suite(source, "testing", session)
if current is not None:
compare = apt_pkg.version_compare(current.version, expected)
if current is None or compare < 0:
# This is still valid, the current version in testing is older than
# the new version we wait for, or there is none in testing yet
# Check if the source we look at is affected by this.
if source in t['packages']:
# The source is affected, lets reject it.
rejectmsg = "{0}: part of the {1} transition.\n\n".format(source, trans)
if current is not None:
currentlymsg = "at version {0}".format(current.version)
else:
currentlymsg = "not present in testing"
rejectmsg += "Transition description: {0}\n\n".format(t["reason"])
rejectmsg += "\n".join(textwrap.wrap("""Your package
is part of a testing transition designed to get {0} migrated (it is
currently {1}, we need version {2}). This transition is managed by the
Release Team, and {3} is the Release-Team member responsible for it.
Please mail debian-release@lists.debian.org or contact {3} directly if you
need further assistance. You might want to upload to experimental until this
transition is done.""".format(source, currentlymsg, expected,t["rm"])))
raise Reject(rejectmsg)
return True
def get_transitions(self):
cnf = Config()
path = cnf.get('Dinstall::ReleaseTransitions', '')
if path == '' or not os.path.exists(path):
return None
contents = file(path, 'r').read()
try:
transitions = yaml.load(contents)
return transitions
except yaml.YAMLError as msg:
utils.warn('Not checking transitions, the transitions file is broken: {0}'.format(msg))
return None
class NoSourceOnlyCheck(Check):
"""Check for source-only upload
......
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
......@@ -42,6 +42,8 @@ import traceback
import commands
import signal
from daklib.gpg import SignedFile
try:
# python >= 2.6
import json
......@@ -2408,60 +2410,6 @@ __all__.append('SrcContents')
################################################################################
from debian.debfile import Deb822
# Temporary Deb822 subclass to fix bugs with : handling; see #597249
class Dak822(Deb822):
def _internal_parser(self, sequence, fields=None):
# The key is non-whitespace, non-colon characters before any colon.
key_part = r"^(?P<key>[^: \t\n\r\f\v]+)\s*:\s*"
single = re.compile(key_part + r"(?P<data>\S.*?)\s*$")
multi = re.compile(key_part + r"$")
multidata = re.compile(r"^\s(?P<data>.+?)\s*$")
wanted_field = lambda f: fields is None or f in fields
if isinstance(sequence, basestring):
sequence = sequence.splitlines()
curkey = None
content = ""
for line in self.gpg_stripped_paragraph(sequence):
m = single.match(line)
if m:
if curkey:
self[curkey] = content
if not wanted_field(m.group('key')):
curkey = None
continue
curkey = m.group('key')
content = m.group('data')
continue
m = multi.match(line)
if m:
if curkey:
self[curkey] = content
if not wanted_field(m.group('key')):
curkey = None
continue
curkey = m.group('key')
content = ""
continue
m = multidata.match(line)
if m:
content += '\n' + line # XXX not m.group('data')?
continue
if curkey:
self[curkey] = content
class DBSource(ORMObject):
def __init__(self, source = None, version = None, maintainer = None, \
changedby = None, poolfile = None, install_date = None, fingerprint = None):
......@@ -2494,7 +2442,9 @@ class DBSource(ORMObject):
@return: fields is the dsc information in a dictionary form
'''
fullpath = self.poolfile.fullpath
fields = Dak822(open(self.poolfile.fullpath, 'r'))
contents = open(fullpath, 'r').read()
signed_file = SignedFile(contents, keyrings=[], require_signature=False)
fields = apt_pkg.TagSection(signed_file.contents)
return fields
metadata = association_proxy('key', 'value')
......
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
......@@ -123,16 +123,22 @@ class FilesystemTransaction(object):
self.actions = []
def copy(self, source, destination, link=True, symlink=False, mode=None):
"""copy `source` to `destination`
"""copy C{source} to C{destination}
Args:
source (str): source file
destination (str): destination file
@type source: str
@param source: source file
Kwargs:
link (bool): Try hardlinking, falling back to copying.
symlink (bool): Create a symlink instead
mode (int): Permissions to change `destination` to.
@type destination: str
@param destination: destination file
@type link: bool
@param link: try hardlinking, falling back to copying
@type symlink: bool
@param symlink: create a symlink instead of copying
@type mode: int
@param mode: permissions to change C{destination} to
"""
if isinstance(mode, str) or isinstance(mode, unicode):
mode = int(mode, 8)
......@@ -140,37 +146,38 @@ class FilesystemTransaction(object):
self.actions.append(_FilesystemCopyAction(source, destination, link=link, symlink=symlink, mode=mode))
def move(self, source, destination, mode=None):
"""move `source` to `destination`
"""move C{source} to C{destination}
@type source: str
@param source: source file
Args:
source (str): source file
destination (str): destination file
@type destination: str
@param destination: destination file
Kwargs:
mode (int): Permissions to change `destination` to.
@type mode: int
@param mode: permissions to change C{destination} to
"""
self.copy(source, destination, link=True, mode=mode)
self.unlink(source)
def unlink(self, path):
"""unlink `path`
"""unlink C{path}
Args:
path (str): file to unlink
@type path: str
@param path: file to unlink
"""
self.actions.append(_FilesystemUnlinkAction(path))
def create(self, path, mode=None):
"""create `filename` and return file handle
"""create C{filename} and return file handle
Args:
filename (str): file to create
@type filename: str
@param filename: file to create
Kwargs:
mode (int): Permissions for the new file
@type mode: int
@param mode: permissions for the new file
Returns:
file handle of the new file
@return: file handle of the new file
"""
if isinstance(mode, str) or isinstance(mode, unicode):
mode = int(mode, 8)
......
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
......@@ -29,31 +29,36 @@ import tempfile
class UploadCopy(object):
"""export a policy queue upload
This class can be used in a with-statements:
This class can be used in a with-statement::
with UploadCopy(...) as copy:
...
Doing so will provide a temporary copy of the upload in the directory
given by the `directory` attribute. The copy will be removed on leaving
given by the C{directory} attribute. The copy will be removed on leaving
the with-block.
Args:
upload (daklib.dbconn.PolicyQueueUpload)
"""
def __init__(self, upload):
"""initializer
@type upload: L{daklib.dbconn.PolicyQueueUpload}
@param upload: upload to handle
"""
self.directory = None
self.upload = upload
def export(self, directory, mode=None, symlink=True):
"""export a copy of the upload
Args:
directory (str)
@type directory: str
@param directory: directory to export to
Kwargs:
mode (int): permissions to use for the copied files
symlink (bool): use symlinks instead of copying the files (default: True)
@type mode: int
@param mode: permissions to use for the copied files
@type symlink: bool
@param symlink: use symlinks instead of copying the files
"""
with FilesystemTransaction() as fs:
source = self.upload.source
......@@ -103,9 +108,10 @@ class PolicyQueueUploadHandler(object):
def __init__(self, upload, session):
"""initializer
Args:
upload (daklib.dbconn.PolicyQueueUpload): upload to process
session: database session
@type upload: L{daklib.dbconn.PolicyQueueUpload}
@param upload: upload to process
@param session: database session
"""
self.upload = upload
self.session = session
......@@ -169,8 +175,8 @@ class PolicyQueueUploadHandler(object):
def reject(self, reason):
"""mark upload as rejected
Args:
reason (str): reason for the rejection
@type reason: str
@param reason: reason for the rejection
"""
fn1 = 'REJECT.{0}'.format(self._changes_prefix)
assert re_file_safe.match(fn1)
......@@ -190,8 +196,8 @@ class PolicyQueueUploadHandler(object):
def get_action(self):
"""get current action
Returns:
string giving the current action, one of 'ACCEPT', 'ACCEPTED', 'REJECT'
@rtype: str
@return: string giving the current action, one of 'ACCEPT', 'ACCEPTED', 'REJECT'
"""
changes_prefix = self._changes_prefix
......@@ -206,18 +212,19 @@ class PolicyQueueUploadHandler(object):
def missing_overrides(self, hints=None):
"""get missing override entries for the upload
Kwargs:
hints (list of dict): suggested hints for new overrides in the same
format as the return value
Returns:
list of dicts with the following keys:
package: package name
priority: default priority (from upload)
section: default section (from upload)
component: default component (from upload)
type: type of required override ('dsc', 'deb' or 'udeb')
All values are strings.
@type hints: list of dict
@param hints: suggested hints for new overrides in the same format as
the return value
@return: list of dicts with the following keys:
- package: package name
- priority: default priority (from upload)
- section: default section (from upload)
- component: default component (from upload)
- type: type of required override ('dsc', 'deb' or 'udeb')
All values are strings.
"""
# TODO: use Package-List field
missing = []
......
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
......@@ -17,7 +17,7 @@
"""module to handle uploads not yet installed to the archive
This module provides classes to handle uploads not yet installed to the
archive. Central is the `Changes` class which represents a changes file.
archive. Central is the L{Changes} class which represents a changes file.
It provides methods to access the included binary and source packages.
"""
......@@ -25,8 +25,9 @@ import apt_inst
import apt_pkg
import os
import re
from .gpg import SignedFile
from .regexes import *
from daklib.gpg import SignedFile
from daklib.regexes import *
class InvalidChangesException(Exception):
pass
......@@ -54,35 +55,52 @@ class InvalidFilenameException(Exception):
class HashedFile(object):
"""file with checksums
Attributes:
filename (str): name of the file
size (long): size in bytes
md5sum (str): MD5 hash in hexdigits
sha1sum (str): SHA1 hash in hexdigits
sha256sum (str): SHA256 hash in hexdigits
section (str): section or None
priority (str): priority or None
"""
def __init__(self, filename, size, md5sum, sha1sum, sha256sum, section=None, priority=None):
self.filename = filename
"""name of the file
@type: str
"""
self.size = size
"""size in bytes
@type: long
"""
self.md5sum = md5sum
"""MD5 hash in hexdigits
@type: str
"""
self.sha1sum = sha1sum
"""SHA1 hash in hexdigits
@type: str
"""
self.sha256sum = sha256sum
"""SHA256 hash in hexdigits
@type: str
"""
self.section = section
"""section or C{None}
@type: str or C{None}
"""
self.priority = priority
"""priority or C{None}
@type: str of C{None}
"""
def check(self, directory):
"""Validate hashes
Check if size and hashes match the expected value.
Args:
directory (str): directory the file is located in
@type directory: str
@param directory: directory the file is located in
Raises:
InvalidHashException: hash mismatch
@raise InvalidHashException: hash mismatch
"""
path = os.path.join(directory, self.filename)
fh = open(path, 'r')
......@@ -108,15 +126,17 @@ class HashedFile(object):
def parse_file_list(control, has_priority_and_section):
"""Parse Files and Checksums-* fields
Args:
control (dict-like): control file to take fields from
has_priority_and_section (bool): Files include section and priority (as in .changes)
@type control: dict-like
@param control: control file to take fields from
@type has_priority_and_section: bool
@param has_priority_and_section: Files field include section and priority
(as in .changes)
Raises:
InvalidChangesException: missing fields or other grave errors
@raise InvalidChangesException: missing fields or other grave errors
Returns:
dictonary mapping filenames to `daklib.upload.HashedFile` objects
@rtype: dict
@return: dict mapping filenames to L{daklib.upload.HashedFile} objects
"""
entries = {}
......@@ -172,32 +192,28 @@ def parse_file_list(control, has_priority_and_section):
class Changes(object):
"""Representation of a .changes file
Attributes:
architectures (list of str): list of architectures included in the upload
binaries (list of daklib.upload.Binary): included binary packages
binary_names (list of str): names of included binary packages
byhand_files (list of daklib.upload.HashedFile): included byhand files
bytes (int): total size of files included in this upload in bytes
changes (dict-like): dict to access fields of the .changes file
closed_bugs (list of str): list of bugs closed by this upload
directory (str): directory the .changes is located in
distributions (list of str): list of target distributions for the upload
filename (str): name of the .changes file
files (dict): dict mapping filenames to daklib.upload.HashedFile objects
path (str): path to the .changes files
primary_fingerprint (str): fingerprint of the PGP key used for the signature
source (daklib.upload.Source or None): included source
valid_signature (bool): True if the changes has a valid signature
"""
def __init__(self, directory, filename, keyrings, require_signature=True):
if not re_file_safe.match(filename):
raise InvalidChangesException('{0}: unsafe filename'.format(filename))
self.directory = directory
"""directory the .changes is located in
@type: str
"""
self.filename = filename
"""name of the .changes file
@type: str
"""
data = open(self.path).read()
self._signed_file = SignedFile(data, keyrings, require_signature)
self.changes = apt_pkg.TagSection(self._signed_file.contents)
"""dict to access fields of the .changes file
@type: dict-like
"""
self._binaries = None
self._source = None
self._files = None
......@@ -206,26 +222,44 @@ class Changes(object):
@property
def path(self):
"""path to the .changes file
@type: str
"""
return os.path.join(self.directory, self.filename)
@property
def primary_fingerprint(self):
"""fingerprint of the key used for signing the .changes file
@type: str
"""
return self._signed_file.primary_fingerprint
@property
def valid_signature(self):
"""C{True} if the .changes has a valid signature
@type: bool
"""
return self._signed_file.valid
@property
def architectures(self):
"""list of architectures included in the upload
@type: list of str
"""
return self.changes['Architecture'].split()
@property
def distributions(self):
"""list of target distributions for the upload
@type: list of str
"""
return self.changes['Distribution'].split()
@property
def source(self):
"""included source or C{None}
@type: L{daklib.upload.Source} or C{None}
"""
if self._source is None:
source_files = []
for f in self.files.itervalues():
......@@ -237,6 +271,9 @@ class Changes(object):
@property
def binaries(self):
"""included binary packages
@type: list of L{daklib.upload.Binary}
"""
if self._binaries is None:
binaries = []
for f in self.files.itervalues():
......@@ -247,6 +284,9 @@ class Changes(object):
@property
def byhand_files(self):
"""included byhand files
@type: list of L{daklib.upload.HashedFile}
"""
byhand = []
for f in self.files.itervalues():
......@@ -260,35 +300,47 @@ class Changes(object):
@property
def binary_names(self):
"""names of included binary packages
@type: list of str
"""
return self.changes['Binary'].split()
@property
def closed_bugs(self):
"""bugs closed by this upload
@type: list of str
"""
return self.changes.get('Closes', '').split()
@property
def files(self):
"""dict mapping filenames to L{daklib.upload.HashedFile} objects
@type: dict
"""
if self._files is None:
self._files = parse_file_list(self.changes, True)
return self._files
@property
def bytes(self):
"""total size of files included in this upload in bytes
@type: number
"""
count = 0
for f in self.files.itervalues():
count += f.size
return count
def __cmp__(self, other):
"""Compare two changes packages
"""compare two changes files
We sort by source name and version first. If these are identical,
we sort changes that include source before those without source (so
that sourceful uploads get processed first), and finally fall back
to the filename (this should really never happen).
Returns:
-1 if self < other, 0 if self == other, 1 if self > other
@rtype: number
@return: n where n < 0 if self < other, n = 0 if self == other, n > 0 if self > other
"""
ret = cmp(self.changes.get('Source'), other.changes.get('Source'))
......@@ -313,25 +365,25 @@ class Changes(object):
class Binary(object):
"""Representation of a binary package
Attributes:
component (str): component name
control (dict-like): dict to access fields in DEBIAN/control
hashed_file (HashedFile): HashedFile object for the .deb
"""
def __init__(self, directory, hashed_file):
self.hashed_file = hashed_file
"""file object for the .deb
@type: HashedFile
"""
path = os.path.join(directory, hashed_file.filename)
data = apt_inst.DebFile(path).control.extractdata("control")
self.control = apt_pkg.TagSection(data)
"""dict to access fields in DEBIAN/control
@type: dict-like
"""
@property
def source(self):
"""Get source package name and version
Returns:
tuple containing source package name and version
"""get tuple with source package name and version
@type: tuple of str
"""
source = self.control.get("Source", None)
if source is None:
......@@ -346,10 +398,8 @@ class Binary(object):
@property
def type(self):
"""Get package type
Returns:
String with the package type ('deb' or 'udeb')
"""package type ('deb' or 'udeb')
@type: str
"""
match = re_file_binary.match(self.hashed_file.filename)
if not match:
......@@ -358,6 +408,9 @@ class Binary(object):
@property
def component(self):
"""component name
@type: str
"""
fields = self.control['Section'].split('/')
if len(fields) > 1:
return fields[0]
......@@ -365,18 +418,13 @@ class Binary(object):
class Source(object):
"""Representation of a source package
Attributes:
component (str): guessed component name. Might be wrong!
dsc (dict-like): dict to access fields in the .dsc file
hashed_files (list of daklib.upload.HashedFile): list of source files (including .dsc)
files (dict): dictonary mapping filenames to HashedFile objects for
additional source files (not including .dsc)
primary_fingerprint (str): fingerprint of the PGP key used for the signature
valid_signature (bool): True if the dsc has a valid signature
"""
def __init__(self, directory, hashed_files, keyrings, require_signature=True):
self.hashed_files = hashed_files
"""list of source files (including the .dsc itself)
@type: list of L{HashedFile}
"""
self._dsc_file = None
for f in hashed_files:
if re_file_dsc.match(f.filename):
......@@ -388,24 +436,46 @@ class Source(object):
data = open(dsc_file_path, 'r').read()
self._signed_file = SignedFile(data, keyrings, require_signature)
self.dsc = apt_pkg.TagSection(self._signed_file.contents)
"""dict to access fields in the .dsc file
@type: dict-like
"""
self._files = None
@property
def files(self):
"""dict mapping filenames to L{HashedFile} objects for additional source files
This list does not include the .dsc itself.
@type: dict
"""
if self._files is None:
self._files = parse_file_list(self.dsc, False)
return self._files
@property
def primary_fingerprint(self):
"""fingerprint of the key used to sign the .dsc
@type: str
"""
return self._signed_file.primary_fingerprint
@property
def valid_signature(self):
"""C{True} if the .dsc has a valid signature
@type: bool
"""
return self._signed_file.valid
@property
def component(self):
"""guessed component name
Might be wrong. Don't rely on this.
@type: str
"""
if 'Section' not in self.dsc:
return 'main'
fields = self.dsc['Section'].split('/')
......
......@@ -1550,7 +1550,6 @@ def get_packages_from_ftp(root, suite, component, architecture):
@rtype: TagFile
@return: apt_pkg class containing package data
"""
filename = "%s/dists/%s/%s/binary-%s/Packages.gz" % (root, suite, component, architecture)
(fd, temp_file) = temp_filename()
......@@ -1576,15 +1575,20 @@ def deb_extract_control(fh):
################################################################################
def mail_addresses_for_upload(maintainer, changed_by, fingerprint):
"""Mail addresses to contact for an upload
"""mail addresses to contact for an upload
@type maintainer: str
@param maintainer: Maintainer field of the .changes file
Args:
maintainer (str): Maintainer field of the changes file
changed_by (str): Changed-By field of the changes file
fingerprint (str): Fingerprint of the PGP key used to sign the upload
@type changed_by: str
@param changed_by: Changed-By field of the .changes file
Returns:
List of RFC 2047-encoded mail addresses to contact regarding this upload
@type fingerprint: str
@param fingerprint: fingerprint of the key used to sign the upload
@rtype: list of str
@return: list of RFC 2047-encoded mail addresses to contact regarding
this upload
"""
addresses = [maintainer]
if changed_by != maintainer:
......@@ -1600,14 +1604,16 @@ def mail_addresses_for_upload(maintainer, changed_by, fingerprint):
################################################################################
def call_editor(text="", suffix=".txt"):
"""Run editor and return the result as a string
"""run editor and return the result as a string
@type text: str
@param text: initial text
Kwargs:
text (str): initial text
suffix (str): extension for temporary file
@type suffix: str
@param suffix: extension for temporary file
Returns:
string with the edited text
@rtype: str
@return: string with the edited text
"""
editor = os.environ.get('VISUAL', os.environ.get('EDITOR', 'vi'))
tmp = tempfile.NamedTemporaryFile(suffix=suffix, delete=False)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册