2012-01-11 17:46:47 +00:00
|
|
|
import collections
|
2012-01-11 16:52:29 +00:00
|
|
|
import os.path
|
|
|
|
import re
|
2018-06-30 23:03:49 +00:00
|
|
|
import unittest
|
2012-01-11 16:52:29 +00:00
|
|
|
|
2012-01-11 17:16:00 +00:00
|
|
|
from . import utils
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2012-01-11 18:03:27 +00:00
|
|
|
|
2008-05-01 07:54:28 +00:00
|
|
|
class Changelog(list):
|
2018-04-07 16:12:33 +00:00
|
|
|
_top_rules = r"""
|
2008-05-01 07:54:28 +00:00
|
|
|
^
|
|
|
|
(?P<source>
|
|
|
|
\w[-+0-9a-z.]+
|
|
|
|
)
|
|
|
|
\
|
|
|
|
\(
|
|
|
|
(?P<version>
|
|
|
|
[^\(\)\ \t]+
|
|
|
|
)
|
|
|
|
\)
|
|
|
|
\s+
|
|
|
|
(?P<distribution>
|
|
|
|
[-+0-9a-zA-Z.]+
|
|
|
|
)
|
2016-05-06 20:28:43 +00:00
|
|
|
\;\s+urgency=
|
|
|
|
(?P<urgency>
|
|
|
|
\w+
|
|
|
|
)
|
2018-04-24 19:20:41 +00:00
|
|
|
(?:,|\n)
|
2008-05-01 07:54:28 +00:00
|
|
|
"""
|
2018-04-07 16:12:33 +00:00
|
|
|
_top_re = re.compile(_top_rules, re.X)
|
|
|
|
_bottom_rules = r"""
|
|
|
|
^
|
|
|
|
\ --\
|
|
|
|
(?P<maintainer>
|
|
|
|
\S(?:\ ?\S)*
|
|
|
|
)
|
|
|
|
\ \
|
|
|
|
(?P<date>
|
|
|
|
(.*)
|
|
|
|
)
|
|
|
|
\n
|
|
|
|
"""
|
|
|
|
_bottom_re = re.compile(_bottom_rules, re.X)
|
|
|
|
_ignore_re = re.compile(r'^(?: |\s*\n)')
|
2008-05-01 07:54:28 +00:00
|
|
|
|
|
|
|
class Entry(object):
|
2018-04-07 16:12:33 +00:00
|
|
|
__slot__ = 'distribution', 'source', 'version', 'urgency', 'maintainer', 'date'
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2018-04-07 16:12:33 +00:00
|
|
|
def __init__(self, **kwargs):
|
|
|
|
for key, value in kwargs.items():
|
|
|
|
setattr(self, key, value)
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2018-04-06 10:13:37 +00:00
|
|
|
def __init__(self, dir='', version=None, file=None):
|
2008-05-01 07:54:28 +00:00
|
|
|
if version is None:
|
|
|
|
version = Version
|
2018-04-06 10:13:37 +00:00
|
|
|
if file:
|
|
|
|
self._parse(version, file)
|
|
|
|
else:
|
|
|
|
with open(os.path.join(dir, "debian/changelog"), encoding="UTF-8") as f:
|
|
|
|
self._parse(version, f)
|
|
|
|
|
|
|
|
def _parse(self, version, f):
|
2018-04-07 16:12:33 +00:00
|
|
|
top_match = None
|
|
|
|
line_no = 0
|
|
|
|
|
|
|
|
for line in f:
|
|
|
|
line_no += 1
|
|
|
|
|
|
|
|
if self._ignore_re.match(line):
|
|
|
|
pass
|
|
|
|
elif top_match is None:
|
|
|
|
top_match = self._top_re.match(line)
|
|
|
|
if not top_match:
|
|
|
|
raise Exception('invalid top line %d in changelog' % line_no)
|
|
|
|
try:
|
|
|
|
v = version(top_match.group('version'))
|
|
|
|
except Exception:
|
|
|
|
if not len(self):
|
|
|
|
raise
|
|
|
|
v = Version(top_match.group('version'))
|
|
|
|
else:
|
|
|
|
bottom_match = self._bottom_re.match(line)
|
|
|
|
if not bottom_match:
|
|
|
|
raise Exception('invalid bottom line %d in changelog' % line_no)
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2018-04-07 16:12:33 +00:00
|
|
|
self.append(self.Entry(distribution=top_match.group('distribution'),
|
|
|
|
source=top_match.group('source'),
|
|
|
|
version=v,
|
|
|
|
urgency=top_match.group('urgency'),
|
|
|
|
maintainer=bottom_match.group('maintainer'),
|
|
|
|
date=bottom_match.group('date')))
|
|
|
|
top_match = bottom_match = None
|
2012-01-11 16:52:29 +00:00
|
|
|
|
2008-05-01 07:54:28 +00:00
|
|
|
class Version(object):
|
2018-06-30 23:16:04 +00:00
|
|
|
_epoch_re = re.compile(r'\d+$')
|
|
|
|
_upstream_re = re.compile(r'[0-9][A-Za-z0-9.+\-:~]*$')
|
|
|
|
_revision_re = re.compile(r'[A-Za-z0-9+.~]+$')
|
2008-05-01 07:54:28 +00:00
|
|
|
|
|
|
|
def __init__(self, version):
|
2018-06-30 23:16:04 +00:00
|
|
|
try:
|
|
|
|
split = version.index(':')
|
|
|
|
except ValueError:
|
|
|
|
epoch, rest = None, version
|
|
|
|
else:
|
|
|
|
epoch, rest = version[0:split], version[split+1:]
|
|
|
|
try:
|
|
|
|
split = rest.rindex('-')
|
|
|
|
except ValueError:
|
|
|
|
upstream, revision = rest, None
|
|
|
|
else:
|
|
|
|
upstream, revision = rest[0:split], rest[split+1:]
|
|
|
|
if ((epoch is not None and not self._epoch_re.match(epoch)) or
|
|
|
|
not self._upstream_re.match(upstream) or
|
|
|
|
(revision is not None and not self._revision_re.match(revision))):
|
2012-01-11 18:03:27 +00:00
|
|
|
raise RuntimeError(u"Invalid debian version")
|
2018-06-30 23:16:04 +00:00
|
|
|
self.epoch = epoch and int(epoch)
|
|
|
|
self.upstream = upstream
|
|
|
|
self.revision = revision
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2013-06-02 20:51:26 +00:00
|
|
|
def __str__(self):
|
2014-01-02 22:18:35 +00:00
|
|
|
return self.complete
|
2013-06-02 20:51:26 +00:00
|
|
|
|
2008-05-01 07:54:28 +00:00
|
|
|
@property
|
|
|
|
def complete(self):
|
|
|
|
if self.epoch is not None:
|
2012-01-11 18:03:27 +00:00
|
|
|
return u"%d:%s" % (self.epoch, self.complete_noepoch)
|
2008-05-01 07:54:28 +00:00
|
|
|
return self.complete_noepoch
|
|
|
|
|
|
|
|
@property
|
|
|
|
def complete_noepoch(self):
|
|
|
|
if self.revision is not None:
|
2012-01-11 18:03:27 +00:00
|
|
|
return u"%s-%s" % (self.upstream, self.revision)
|
2008-05-01 07:54:28 +00:00
|
|
|
return self.upstream
|
|
|
|
|
|
|
|
@property
|
|
|
|
def debian(self):
|
|
|
|
from warnings import warn
|
2012-01-11 18:03:27 +00:00
|
|
|
warn(u"debian argument was replaced by revision", DeprecationWarning, stacklevel=2)
|
2008-05-01 07:54:28 +00:00
|
|
|
return self.revision
|
|
|
|
|
2012-01-11 16:52:29 +00:00
|
|
|
|
2018-06-30 23:03:49 +00:00
|
|
|
class _VersionTest(unittest.TestCase):
|
|
|
|
def test_native(self):
|
|
|
|
v = Version('1.2+c~4')
|
|
|
|
self.assertEqual(v.epoch, None)
|
|
|
|
self.assertEqual(v.upstream, '1.2+c~4')
|
|
|
|
self.assertEqual(v.revision, None)
|
|
|
|
self.assertEqual(v.complete, '1.2+c~4')
|
|
|
|
self.assertEqual(v.complete_noepoch, '1.2+c~4')
|
|
|
|
|
|
|
|
def test_nonnative(self):
|
|
|
|
v = Version('1-2+d~3')
|
|
|
|
self.assertEqual(v.epoch, None)
|
|
|
|
self.assertEqual(v.upstream, '1')
|
|
|
|
self.assertEqual(v.revision, '2+d~3')
|
|
|
|
self.assertEqual(v.complete, '1-2+d~3')
|
|
|
|
self.assertEqual(v.complete_noepoch, '1-2+d~3')
|
|
|
|
|
|
|
|
def test_native_epoch(self):
|
|
|
|
v = Version('5:1.2.3')
|
|
|
|
self.assertEqual(v.epoch, 5)
|
|
|
|
self.assertEqual(v.upstream, '1.2.3')
|
|
|
|
self.assertEqual(v.revision, None)
|
|
|
|
self.assertEqual(v.complete, '5:1.2.3')
|
|
|
|
self.assertEqual(v.complete_noepoch, '1.2.3')
|
|
|
|
|
|
|
|
def test_nonnative_epoch(self):
|
|
|
|
v = Version('5:1.2.3-4')
|
|
|
|
self.assertEqual(v.epoch, 5)
|
|
|
|
self.assertEqual(v.upstream, '1.2.3')
|
|
|
|
self.assertEqual(v.revision, '4')
|
|
|
|
self.assertEqual(v.complete, '5:1.2.3-4')
|
|
|
|
self.assertEqual(v.complete_noepoch, '1.2.3-4')
|
|
|
|
|
|
|
|
def test_multi_hyphen(self):
|
|
|
|
v = Version('1-2-3')
|
|
|
|
self.assertEqual(v.epoch, None)
|
|
|
|
self.assertEqual(v.upstream, '1-2')
|
|
|
|
self.assertEqual(v.revision, '3')
|
|
|
|
self.assertEqual(v.complete, '1-2-3')
|
|
|
|
|
|
|
|
def test_multi_colon(self):
|
|
|
|
v = Version('1:2:3')
|
|
|
|
self.assertEqual(v.epoch, 1)
|
|
|
|
self.assertEqual(v.upstream, '2:3')
|
|
|
|
self.assertEqual(v.revision, None)
|
|
|
|
|
|
|
|
def test_invalid_epoch(self):
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
|
|
v = Version('a:1')
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
|
|
v = Version('-1:1')
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
|
|
v = Version('1a:1')
|
|
|
|
|
|
|
|
def test_invalid_upstream(self):
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
|
|
v = Version('1_2')
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
|
|
v = Version('1/2')
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
|
|
v = Version('a1')
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
|
|
v = Version('1 2')
|
|
|
|
|
|
|
|
def test_invalid_revision(self):
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
|
|
v = Version('1-2_3')
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
|
|
v = Version('1-2/3')
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
|
|
v = Version('1-2:3')
|
|
|
|
|
|
|
|
|
2008-05-01 07:54:28 +00:00
|
|
|
class VersionLinux(Version):
|
2014-01-01 22:14:11 +00:00
|
|
|
_version_linux_rules = r"""
|
2008-05-01 07:54:28 +00:00
|
|
|
^
|
|
|
|
(?P<version>
|
2011-10-28 18:47:00 +00:00
|
|
|
\d+\.\d+
|
2008-05-01 07:54:28 +00:00
|
|
|
)
|
2012-06-02 20:17:17 +00:00
|
|
|
(?P<update>
|
2014-11-08 15:41:20 +00:00
|
|
|
(?:\.\d+)?
|
|
|
|
(?:-[a-z]+\d+)?
|
|
|
|
)
|
2008-05-01 07:54:28 +00:00
|
|
|
(?:
|
|
|
|
~
|
|
|
|
(?P<modifier>
|
|
|
|
.+?
|
|
|
|
)
|
|
|
|
)?
|
|
|
|
(?:
|
|
|
|
\.dfsg\.
|
|
|
|
(?P<dfsg>
|
|
|
|
\d+
|
|
|
|
)
|
|
|
|
)?
|
|
|
|
-
|
2009-11-24 18:30:25 +00:00
|
|
|
\d+
|
2009-11-24 18:35:28 +00:00
|
|
|
(\.\d+)?
|
2009-11-24 18:30:25 +00:00
|
|
|
(?:
|
|
|
|
(?P<revision_experimental>
|
2013-06-02 20:54:29 +00:00
|
|
|
~exp\d+
|
2009-11-24 18:30:25 +00:00
|
|
|
)
|
|
|
|
|
|
2015-04-27 19:44:59 +00:00
|
|
|
(?P<revision_security>
|
2018-07-01 02:59:58 +00:00
|
|
|
(?:[~+]deb\d+u\d+)+
|
2015-04-27 19:44:59 +00:00
|
|
|
)?
|
2013-08-11 21:30:14 +00:00
|
|
|
(?P<revision_backports>
|
2015-04-27 19:33:39 +00:00
|
|
|
~bpo\d+\+\d+
|
2015-04-27 19:44:59 +00:00
|
|
|
)?
|
2013-08-11 21:30:14 +00:00
|
|
|
|
|
2009-11-24 18:30:25 +00:00
|
|
|
(?P<revision_other>
|
2018-07-01 03:35:00 +00:00
|
|
|
[^-]+?
|
2009-11-24 18:35:28 +00:00
|
|
|
)
|
2015-04-27 19:44:59 +00:00
|
|
|
)
|
2018-04-24 19:40:27 +00:00
|
|
|
(?:\+b\d+)?
|
2008-05-01 07:54:28 +00:00
|
|
|
$
|
|
|
|
"""
|
|
|
|
_version_linux_re = re.compile(_version_linux_rules, re.X)
|
|
|
|
|
|
|
|
def __init__(self, version):
|
|
|
|
super(VersionLinux, self).__init__(version)
|
|
|
|
match = self._version_linux_re.match(version)
|
|
|
|
if match is None:
|
2012-01-11 18:03:27 +00:00
|
|
|
raise RuntimeError(u"Invalid debian linux version")
|
2008-05-01 07:54:28 +00:00
|
|
|
d = match.groupdict()
|
|
|
|
self.linux_modifier = d['modifier']
|
|
|
|
self.linux_version = d['version']
|
|
|
|
if d['modifier'] is not None:
|
2012-06-02 20:17:17 +00:00
|
|
|
assert not d['update']
|
2015-08-29 20:48:39 +00:00
|
|
|
self.linux_upstream = '-'.join((d['version'], d['modifier']))
|
2008-05-01 07:54:28 +00:00
|
|
|
else:
|
|
|
|
self.linux_upstream = d['version']
|
2014-11-08 15:41:20 +00:00
|
|
|
self.linux_upstream_full = self.linux_upstream + d['update']
|
2008-05-01 07:54:28 +00:00
|
|
|
self.linux_dfsg = d['dfsg']
|
2009-11-24 18:30:25 +00:00
|
|
|
self.linux_revision_experimental = match.group('revision_experimental') and True
|
2015-04-27 19:44:59 +00:00
|
|
|
self.linux_revision_security = match.group('revision_security') and True
|
2013-08-11 21:30:14 +00:00
|
|
|
self.linux_revision_backports = match.group('revision_backports') and True
|
2009-11-24 18:30:25 +00:00
|
|
|
self.linux_revision_other = match.group('revision_other') and True
|
2012-01-11 16:52:29 +00:00
|
|
|
|
|
|
|
|
2018-07-01 02:59:07 +00:00
|
|
|
class _VersionLinuxTest(unittest.TestCase):
|
|
|
|
def test_stable(self):
|
|
|
|
v = VersionLinux('1.2.3-4')
|
|
|
|
self.assertEqual(v.linux_version, '1.2')
|
|
|
|
self.assertEqual(v.linux_upstream, '1.2')
|
|
|
|
self.assertEqual(v.linux_upstream_full, '1.2.3')
|
|
|
|
self.assertEqual(v.linux_modifier, None)
|
|
|
|
self.assertEqual(v.linux_dfsg, None)
|
|
|
|
self.assertFalse(v.linux_revision_experimental)
|
|
|
|
self.assertFalse(v.linux_revision_security)
|
|
|
|
self.assertFalse(v.linux_revision_backports)
|
|
|
|
self.assertFalse(v.linux_revision_other)
|
|
|
|
|
|
|
|
def test_rc(self):
|
|
|
|
v = VersionLinux('1.2~rc3-4')
|
|
|
|
self.assertEqual(v.linux_version, '1.2')
|
|
|
|
self.assertEqual(v.linux_upstream, '1.2-rc3')
|
|
|
|
self.assertEqual(v.linux_upstream_full, '1.2-rc3')
|
|
|
|
self.assertEqual(v.linux_modifier, 'rc3')
|
|
|
|
self.assertEqual(v.linux_dfsg, None)
|
|
|
|
self.assertFalse(v.linux_revision_experimental)
|
|
|
|
self.assertFalse(v.linux_revision_security)
|
|
|
|
self.assertFalse(v.linux_revision_backports)
|
|
|
|
self.assertFalse(v.linux_revision_other)
|
|
|
|
|
|
|
|
def test_dfsg(self):
|
|
|
|
v = VersionLinux('1.2~rc3.dfsg.1-4')
|
|
|
|
self.assertEqual(v.linux_version, '1.2')
|
|
|
|
self.assertEqual(v.linux_upstream, '1.2-rc3')
|
|
|
|
self.assertEqual(v.linux_upstream_full, '1.2-rc3')
|
|
|
|
self.assertEqual(v.linux_modifier, 'rc3')
|
|
|
|
self.assertEqual(v.linux_dfsg, '1')
|
|
|
|
self.assertFalse(v.linux_revision_experimental)
|
|
|
|
self.assertFalse(v.linux_revision_security)
|
|
|
|
self.assertFalse(v.linux_revision_backports)
|
|
|
|
self.assertFalse(v.linux_revision_other)
|
|
|
|
|
|
|
|
def test_experimental(self):
|
|
|
|
v = VersionLinux('1.2~rc3-4~exp5')
|
|
|
|
self.assertEqual(v.linux_upstream_full, '1.2-rc3')
|
|
|
|
self.assertTrue(v.linux_revision_experimental)
|
|
|
|
self.assertFalse(v.linux_revision_security)
|
|
|
|
self.assertFalse(v.linux_revision_backports)
|
|
|
|
self.assertFalse(v.linux_revision_other)
|
|
|
|
|
|
|
|
def test_security(self):
|
|
|
|
v = VersionLinux('1.2.3-4+deb10u1')
|
|
|
|
self.assertEqual(v.linux_upstream_full, '1.2.3')
|
|
|
|
self.assertFalse(v.linux_revision_experimental)
|
|
|
|
self.assertTrue(v.linux_revision_security)
|
|
|
|
self.assertFalse(v.linux_revision_backports)
|
|
|
|
self.assertFalse(v.linux_revision_other)
|
|
|
|
|
|
|
|
def test_backports(self):
|
|
|
|
v = VersionLinux('1.2.3-4~bpo9+10')
|
|
|
|
self.assertEqual(v.linux_upstream_full, '1.2.3')
|
|
|
|
self.assertFalse(v.linux_revision_experimental)
|
|
|
|
self.assertFalse(v.linux_revision_security)
|
|
|
|
self.assertTrue(v.linux_revision_backports)
|
|
|
|
self.assertFalse(v.linux_revision_other)
|
|
|
|
|
|
|
|
def test_security_backports(self):
|
|
|
|
v = VersionLinux('1.2.3-4+deb10u1~bpo9+10')
|
|
|
|
self.assertEqual(v.linux_upstream_full, '1.2.3')
|
|
|
|
self.assertFalse(v.linux_revision_experimental)
|
|
|
|
self.assertTrue(v.linux_revision_security)
|
|
|
|
self.assertTrue(v.linux_revision_backports)
|
|
|
|
self.assertFalse(v.linux_revision_other)
|
|
|
|
|
|
|
|
def test_lts_backports(self):
|
|
|
|
# Backport during LTS, as an extra package in the -security
|
|
|
|
# suite. Since this is not part of a -backports suite it
|
|
|
|
# shouldn't get the linux_revision_backports flag.
|
|
|
|
v = VersionLinux('1.2.3-4~deb9u10')
|
|
|
|
self.assertEqual(v.linux_upstream_full, '1.2.3')
|
|
|
|
self.assertFalse(v.linux_revision_experimental)
|
|
|
|
self.assertTrue(v.linux_revision_security)
|
|
|
|
self.assertFalse(v.linux_revision_backports)
|
|
|
|
self.assertFalse(v.linux_revision_other)
|
|
|
|
|
|
|
|
def test_lts_backports_2(self):
|
|
|
|
# Same but with two security extensions in the revision.
|
|
|
|
v = VersionLinux('1.2.3-4+deb10u1~deb9u10')
|
|
|
|
self.assertEqual(v.linux_upstream_full, '1.2.3')
|
|
|
|
self.assertFalse(v.linux_revision_experimental)
|
|
|
|
self.assertTrue(v.linux_revision_security)
|
|
|
|
self.assertFalse(v.linux_revision_backports)
|
|
|
|
self.assertFalse(v.linux_revision_other)
|
|
|
|
|
2018-07-01 03:23:20 +00:00
|
|
|
def test_binnmu(self):
|
|
|
|
v = VersionLinux('1.2.3-4+b1')
|
|
|
|
self.assertFalse(v.linux_revision_experimental)
|
|
|
|
self.assertFalse(v.linux_revision_security)
|
|
|
|
self.assertFalse(v.linux_revision_backports)
|
|
|
|
self.assertFalse(v.linux_revision_other)
|
|
|
|
|
|
|
|
def test_other_revision(self):
|
|
|
|
v = VersionLinux('4.16.5-1+revert+crng+ready') # from #898087
|
|
|
|
self.assertFalse(v.linux_revision_experimental)
|
|
|
|
self.assertFalse(v.linux_revision_security)
|
|
|
|
self.assertFalse(v.linux_revision_backports)
|
|
|
|
self.assertTrue(v.linux_revision_other)
|
|
|
|
|
|
|
|
def test_other_revision_binnmu(self):
|
|
|
|
v = VersionLinux('4.16.5-1+revert+crng+ready+b1')
|
|
|
|
self.assertFalse(v.linux_revision_experimental)
|
|
|
|
self.assertFalse(v.linux_revision_security)
|
|
|
|
self.assertFalse(v.linux_revision_backports)
|
|
|
|
self.assertTrue(v.linux_revision_other)
|
|
|
|
|
2018-07-01 02:59:07 +00:00
|
|
|
|
2012-01-11 17:46:47 +00:00
|
|
|
class PackageArchitecture(collections.MutableSet):
|
|
|
|
__slots__ = '_data'
|
|
|
|
|
2012-01-11 16:52:29 +00:00
|
|
|
def __init__(self, value=None):
|
2012-01-11 17:46:47 +00:00
|
|
|
self._data = set()
|
|
|
|
if value:
|
|
|
|
self.extend(value)
|
|
|
|
|
|
|
|
def __contains__(self, value):
|
|
|
|
return self._data.__contains__(value)
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
return self._data.__iter__()
|
|
|
|
|
|
|
|
def __len__(self):
|
|
|
|
return self._data.__len__()
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2014-01-02 22:18:35 +00:00
|
|
|
def __str__(self):
|
2015-08-29 20:48:39 +00:00
|
|
|
return ' '.join(sorted(self))
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2012-01-11 17:46:47 +00:00
|
|
|
def add(self, value):
|
|
|
|
self._data.add(value)
|
|
|
|
|
|
|
|
def discard(self, value):
|
|
|
|
self._data.discard(value)
|
2008-05-01 07:54:28 +00:00
|
|
|
|
|
|
|
def extend(self, value):
|
2015-08-29 20:48:39 +00:00
|
|
|
if isinstance(value, str):
|
2012-01-11 17:46:47 +00:00
|
|
|
for i in re.split('\s', value.strip()):
|
|
|
|
self.add(i)
|
2008-05-01 07:54:28 +00:00
|
|
|
else:
|
2012-01-11 17:46:47 +00:00
|
|
|
raise RuntimeError
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2012-01-11 16:52:29 +00:00
|
|
|
|
2008-05-01 07:54:28 +00:00
|
|
|
class PackageDescription(object):
|
|
|
|
__slots__ = "short", "long"
|
|
|
|
|
2012-01-11 16:52:29 +00:00
|
|
|
def __init__(self, value=None):
|
2008-06-25 12:54:20 +00:00
|
|
|
self.short = []
|
2008-05-01 07:54:28 +00:00
|
|
|
self.long = []
|
|
|
|
if value is not None:
|
2016-07-14 10:40:35 +00:00
|
|
|
desc_split = value.split("\n", 1)
|
|
|
|
self.append_short(desc_split[0])
|
|
|
|
if len(desc_split) == 2:
|
|
|
|
self.append(desc_split[1])
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2014-01-02 22:18:35 +00:00
|
|
|
def __str__(self):
|
2012-01-11 16:52:29 +00:00
|
|
|
wrap = utils.TextWrapper(width=74, fix_sentence_endings=True).wrap
|
2015-08-29 20:48:39 +00:00
|
|
|
short = ', '.join(self.short)
|
2008-06-25 12:34:38 +00:00
|
|
|
long_pars = []
|
2008-05-01 07:54:28 +00:00
|
|
|
for i in self.long:
|
2008-06-25 12:34:38 +00:00
|
|
|
long_pars.append(wrap(i))
|
2015-08-29 20:48:39 +00:00
|
|
|
long = '\n .\n '.join(['\n '.join(i) for i in long_pars])
|
2016-07-14 10:40:35 +00:00
|
|
|
return short + '\n ' + long if long else short
|
2008-05-01 07:54:28 +00:00
|
|
|
|
|
|
|
def append(self, str):
|
|
|
|
str = str.strip()
|
|
|
|
if str:
|
2012-01-11 18:03:27 +00:00
|
|
|
self.long.extend(str.split(u"\n.\n"))
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2008-06-25 12:54:20 +00:00
|
|
|
def append_short(self, str):
|
2012-01-11 18:03:27 +00:00
|
|
|
for i in [i.strip() for i in str.split(u",")]:
|
2008-06-25 12:54:20 +00:00
|
|
|
if i:
|
|
|
|
self.short.append(i)
|
|
|
|
|
|
|
|
def extend(self, desc):
|
|
|
|
if isinstance(desc, PackageDescription):
|
|
|
|
self.short.extend(desc.short)
|
|
|
|
self.long.extend(desc.long)
|
|
|
|
else:
|
|
|
|
raise TypeError
|
|
|
|
|
2012-01-11 16:52:29 +00:00
|
|
|
|
2008-05-01 07:54:28 +00:00
|
|
|
class PackageRelation(list):
|
2009-09-14 09:42:44 +00:00
|
|
|
def __init__(self, value=None, override_arches=None):
|
|
|
|
if value:
|
|
|
|
self.extend(value, override_arches)
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2014-01-02 22:18:35 +00:00
|
|
|
def __str__(self):
|
2015-08-29 20:48:39 +00:00
|
|
|
return ', '.join(str(i) for i in self)
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2009-09-14 09:27:02 +00:00
|
|
|
def _search_value(self, value):
|
2008-05-01 07:54:28 +00:00
|
|
|
for i in self:
|
2009-09-14 09:27:02 +00:00
|
|
|
if i._search_value(value):
|
2008-05-01 07:54:28 +00:00
|
|
|
return i
|
|
|
|
return None
|
|
|
|
|
2009-09-14 09:42:44 +00:00
|
|
|
def append(self, value, override_arches=None):
|
2015-08-29 20:48:39 +00:00
|
|
|
if isinstance(value, str):
|
2009-09-14 09:42:44 +00:00
|
|
|
value = PackageRelationGroup(value, override_arches)
|
2008-05-01 07:54:28 +00:00
|
|
|
elif not isinstance(value, PackageRelationGroup):
|
2012-01-11 18:03:27 +00:00
|
|
|
raise ValueError(u"got %s" % type(value))
|
2009-09-14 09:27:02 +00:00
|
|
|
j = self._search_value(value)
|
2008-05-01 07:54:28 +00:00
|
|
|
if j:
|
2009-09-14 09:27:02 +00:00
|
|
|
j._update_arches(value)
|
2008-05-01 07:54:28 +00:00
|
|
|
else:
|
|
|
|
super(PackageRelation, self).append(value)
|
|
|
|
|
2009-09-14 09:42:44 +00:00
|
|
|
def extend(self, value, override_arches=None):
|
2015-08-29 20:48:39 +00:00
|
|
|
if isinstance(value, str):
|
|
|
|
value = (j.strip() for j in re.split(',', value.strip()))
|
2008-05-01 07:54:28 +00:00
|
|
|
for i in value:
|
2009-09-14 09:42:44 +00:00
|
|
|
self.append(i, override_arches)
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2012-01-11 16:52:29 +00:00
|
|
|
|
2008-05-01 07:54:28 +00:00
|
|
|
class PackageRelationGroup(list):
|
2009-09-14 09:42:44 +00:00
|
|
|
def __init__(self, value=None, override_arches=None):
|
|
|
|
if value:
|
|
|
|
self.extend(value, override_arches)
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2014-01-02 22:18:35 +00:00
|
|
|
def __str__(self):
|
2015-08-29 20:48:39 +00:00
|
|
|
return ' | '.join(str(i) for i in self)
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2009-09-14 09:27:02 +00:00
|
|
|
def _search_value(self, value):
|
2015-08-29 20:48:39 +00:00
|
|
|
for i, j in zip(self, value):
|
2015-11-10 12:40:25 +00:00
|
|
|
if i.name != j.name or i.operator != j.operator or \
|
2015-11-10 12:41:47 +00:00
|
|
|
i.version != j.version or i.restrictions != j.restrictions:
|
2008-05-01 07:54:28 +00:00
|
|
|
return None
|
|
|
|
return self
|
|
|
|
|
2009-09-14 09:27:02 +00:00
|
|
|
def _update_arches(self, value):
|
2015-08-29 20:48:39 +00:00
|
|
|
for i, j in zip(self, value):
|
2008-05-01 07:54:28 +00:00
|
|
|
if i.arches:
|
|
|
|
for arch in j.arches:
|
|
|
|
if arch not in i.arches:
|
|
|
|
i.arches.append(arch)
|
|
|
|
|
2009-09-14 09:42:44 +00:00
|
|
|
def append(self, value, override_arches=None):
|
2015-08-29 20:48:39 +00:00
|
|
|
if isinstance(value, str):
|
2009-09-14 09:42:44 +00:00
|
|
|
value = PackageRelationEntry(value, override_arches)
|
2008-05-01 07:54:28 +00:00
|
|
|
elif not isinstance(value, PackageRelationEntry):
|
|
|
|
raise ValueError
|
|
|
|
super(PackageRelationGroup, self).append(value)
|
|
|
|
|
2009-09-14 09:42:44 +00:00
|
|
|
def extend(self, value, override_arches=None):
|
2015-08-29 20:48:39 +00:00
|
|
|
if isinstance(value, str):
|
2012-01-11 20:01:55 +00:00
|
|
|
value = (j.strip() for j in re.split('\|', value.strip()))
|
2008-05-01 07:54:28 +00:00
|
|
|
for i in value:
|
2009-09-14 09:42:44 +00:00
|
|
|
self.append(i, override_arches)
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2012-01-11 16:52:29 +00:00
|
|
|
|
2008-05-01 07:54:28 +00:00
|
|
|
class PackageRelationEntry(object):
|
2015-11-10 12:41:47 +00:00
|
|
|
__slots__ = "name", "operator", "version", "arches", "restrictions"
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2015-11-10 12:41:47 +00:00
|
|
|
_re = re.compile(r'^(\S+)(?: \((<<|<=|=|!=|>=|>>)\s*([^)]+)\))?(?: \[([^]]+)\])?(?: <([^>]+)>)?$')
|
2008-05-01 07:54:28 +00:00
|
|
|
|
|
|
|
class _operator(object):
|
2012-01-11 16:52:29 +00:00
|
|
|
OP_LT = 1
|
|
|
|
OP_LE = 2
|
|
|
|
OP_EQ = 3
|
|
|
|
OP_NE = 4
|
|
|
|
OP_GE = 5
|
|
|
|
OP_GT = 6
|
|
|
|
|
|
|
|
operators = {
|
2015-08-29 20:48:39 +00:00
|
|
|
'<<': OP_LT,
|
|
|
|
'<=': OP_LE,
|
|
|
|
'=': OP_EQ,
|
|
|
|
'!=': OP_NE,
|
|
|
|
'>=': OP_GE,
|
|
|
|
'>>': OP_GT,
|
2012-01-11 16:52:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
operators_neg = {
|
|
|
|
OP_LT: OP_GE,
|
|
|
|
OP_LE: OP_GT,
|
|
|
|
OP_EQ: OP_NE,
|
|
|
|
OP_NE: OP_EQ,
|
|
|
|
OP_GE: OP_LT,
|
|
|
|
OP_GT: OP_LE,
|
|
|
|
}
|
|
|
|
|
2014-01-01 22:14:11 +00:00
|
|
|
operators_text = dict((b, a) for a, b in operators.items())
|
2008-05-01 07:54:28 +00:00
|
|
|
|
|
|
|
__slots__ = '_op',
|
|
|
|
|
|
|
|
def __init__(self, value):
|
|
|
|
self._op = self.operators[value]
|
|
|
|
|
|
|
|
def __neg__(self):
|
|
|
|
return self.__class__(self.operators_text[self.operators_neg[self._op]])
|
|
|
|
|
2014-01-02 22:18:35 +00:00
|
|
|
def __str__(self):
|
2008-05-01 07:54:28 +00:00
|
|
|
return self.operators_text[self._op]
|
|
|
|
|
2015-11-10 12:40:25 +00:00
|
|
|
def __eq__(self, other):
|
|
|
|
return type(other) == type(self) and self._op == other._op
|
|
|
|
|
2009-09-14 09:42:44 +00:00
|
|
|
def __init__(self, value=None, override_arches=None):
|
2015-08-29 20:48:39 +00:00
|
|
|
if not isinstance(value, str):
|
2008-05-01 07:54:28 +00:00
|
|
|
raise ValueError
|
|
|
|
|
2009-09-14 09:42:44 +00:00
|
|
|
self.parse(value)
|
|
|
|
|
|
|
|
if override_arches:
|
|
|
|
self.arches = list(override_arches)
|
|
|
|
|
2014-01-02 22:18:35 +00:00
|
|
|
def __str__(self):
|
2008-05-01 07:54:28 +00:00
|
|
|
ret = [self.name]
|
|
|
|
if self.operator is not None and self.version is not None:
|
2015-08-29 20:48:39 +00:00
|
|
|
ret.extend((' (', str(self.operator), ' ', self.version, ')'))
|
2008-05-01 07:54:28 +00:00
|
|
|
if self.arches:
|
2015-08-29 20:48:39 +00:00
|
|
|
ret.extend((' [', ' '.join(self.arches), ']'))
|
2015-11-10 12:41:47 +00:00
|
|
|
if self.restrictions:
|
|
|
|
ret.extend((' <', ' '.join(self.restrictions), '>'))
|
2015-08-29 20:48:39 +00:00
|
|
|
return ''.join(ret)
|
2008-05-01 07:54:28 +00:00
|
|
|
|
|
|
|
def parse(self, value):
|
|
|
|
match = self._re.match(value)
|
|
|
|
if match is None:
|
2012-01-11 18:03:27 +00:00
|
|
|
raise RuntimeError(u"Can't parse dependency %s" % value)
|
2008-05-01 07:54:28 +00:00
|
|
|
match = match.groups()
|
|
|
|
self.name = match[0]
|
|
|
|
if match[1] is not None:
|
|
|
|
self.operator = self._operator(match[1])
|
|
|
|
else:
|
|
|
|
self.operator = None
|
|
|
|
self.version = match[2]
|
|
|
|
if match[3] is not None:
|
|
|
|
self.arches = re.split('\s+', match[3])
|
|
|
|
else:
|
|
|
|
self.arches = []
|
2015-11-10 12:41:47 +00:00
|
|
|
if match[4] is not None:
|
|
|
|
self.restrictions = re.split('\s+', match[4])
|
|
|
|
else:
|
|
|
|
self.restrictions = []
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2012-01-11 16:52:29 +00:00
|
|
|
|
2016-01-08 12:08:28 +00:00
|
|
|
class _ControlFileDict(dict):
|
2008-05-01 07:54:28 +00:00
|
|
|
def __setitem__(self, key, value):
|
|
|
|
try:
|
|
|
|
cls = self._fields[key]
|
|
|
|
if not isinstance(value, cls):
|
|
|
|
value = cls(value)
|
2012-01-11 16:52:29 +00:00
|
|
|
except KeyError:
|
|
|
|
pass
|
2016-01-08 12:08:28 +00:00
|
|
|
super(_ControlFileDict, self).__setitem__(key, value)
|
2008-05-01 07:54:28 +00:00
|
|
|
|
2015-10-29 12:06:25 +00:00
|
|
|
def keys(self):
|
2016-01-08 12:08:28 +00:00
|
|
|
keys = set(super(_ControlFileDict, self).keys())
|
2014-01-02 22:18:35 +00:00
|
|
|
for i in self._fields.keys():
|
2012-01-11 16:52:29 +00:00
|
|
|
if i in self:
|
2008-05-01 07:54:28 +00:00
|
|
|
keys.remove(i)
|
|
|
|
yield i
|
2016-01-08 12:15:30 +00:00
|
|
|
for i in sorted(list(keys)):
|
2008-05-01 07:54:28 +00:00
|
|
|
yield i
|
|
|
|
|
2015-10-29 12:06:25 +00:00
|
|
|
def items(self):
|
|
|
|
for i in self.keys():
|
2008-05-01 07:54:28 +00:00
|
|
|
yield (i, self[i])
|
|
|
|
|
2015-10-29 12:06:25 +00:00
|
|
|
def values(self):
|
|
|
|
for i in self.keys():
|
2008-05-01 07:54:28 +00:00
|
|
|
yield self[i]
|
2015-08-11 21:42:35 +00:00
|
|
|
|
|
|
|
|
2016-01-08 12:08:28 +00:00
|
|
|
class Package(_ControlFileDict):
|
|
|
|
_fields = collections.OrderedDict((
|
|
|
|
('Package', str),
|
|
|
|
('Source', str),
|
|
|
|
('Architecture', PackageArchitecture),
|
|
|
|
('Section', str),
|
|
|
|
('Priority', str),
|
|
|
|
('Maintainer', str),
|
|
|
|
('Uploaders', str),
|
|
|
|
('Standards-Version', str),
|
|
|
|
('Build-Depends', PackageRelation),
|
2017-09-30 13:07:22 +00:00
|
|
|
('Build-Depends-Arch', PackageRelation),
|
2016-01-08 12:08:28 +00:00
|
|
|
('Build-Depends-Indep', PackageRelation),
|
|
|
|
('Provides', PackageRelation),
|
|
|
|
('Pre-Depends', PackageRelation),
|
|
|
|
('Depends', PackageRelation),
|
|
|
|
('Recommends', PackageRelation),
|
|
|
|
('Suggests', PackageRelation),
|
|
|
|
('Replaces', PackageRelation),
|
|
|
|
('Breaks', PackageRelation),
|
|
|
|
('Conflicts', PackageRelation),
|
|
|
|
('Description', PackageDescription),
|
|
|
|
))
|
|
|
|
|
2015-08-11 21:42:35 +00:00
|
|
|
|
2016-01-08 12:08:28 +00:00
|
|
|
class TestsControl(_ControlFileDict):
|
|
|
|
_fields = collections.OrderedDict((
|
|
|
|
('Tests', str),
|
|
|
|
('Test-Command', str),
|
|
|
|
('Restrictions', str),
|
|
|
|
('Features', str),
|
|
|
|
('Depends', PackageRelation),
|
|
|
|
('Tests-Directory', str),
|
|
|
|
('Classes', str),
|
|
|
|
))
|
2018-06-30 23:03:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
unittest.main()
|