Move bugzilla code to a separate file

Fix #358

Previously the bugzilla-specific code was implemented in `tito.common`
which is imported from everywhere. Therefore missing dependency
to `python3-bugzilla` resulted in failure even for simple commands
such as `tito build --tgz`.
This commit is contained in:
Jakub Kadlcik 2020-04-24 18:22:17 +02:00
parent 6e1ebf4a3f
commit 3a62bb6613
6 changed files with 347 additions and 339 deletions

124
src/tito/bugtracker.py Normal file
View file

@ -0,0 +1,124 @@
import os
import re
from bugzilla.rhbugzilla import RHBugzilla
from tito.common import debug, error_out
from tito.exception import TitoException
from tito.compat import xmlrpclib
class MissingBugzillaCredsException(TitoException):
pass
class BugzillaExtractor(object):
"""
Parses output of a dist-git commit diff looking for changelog
entries that look like they reference bugzilla commits.
Optionally can check bugzilla for required flags on each bug.
"""
def __init__(self, diff_output, required_flags=None,
placeholder_bz=None):
self.diff_output = diff_output
self.required_flags = required_flags
self.placeholder_bz = placeholder_bz
# Tuples of bugzilla ID + commit message we extracted:
self.bzs = []
def extract(self):
self.bzs = self._extract_bzs()
if self.required_flags:
self._check_for_bugzilla_creds()
self.bzs = self._filter_bzs_with_flags()
return self._format_lines()
def _check_for_bugzilla_creds(self):
if not os.path.exists(os.path.expanduser("~/.bugzillarc")):
raise MissingBugzillaCredsException("Missing ~/.bugzillarc")
else:
debug("Found bugzilla credentials in ~/.bugzillarc")
def _extract_bzs(self):
"""
Parses the output of CVS diff or a series of git commit log entries,
looking for new lines which look like a commit of the format:
######: Commit message
Returns a list of lines of text similar to:
Resolves: #XXXXXX - Commit message
If the releaser specifies any required bugzilla flags we will
check each bug found and see if it has all required flags. If not
we skip it. If we end up with *no* bugs with the required flags
our build is likely to fail, so we look for a placeholder bugzilla
defined in relaser config and use that instead if possible, otherwise
error out.
Returns a list of lines to write to the commit message as is.
"""
regex = re.compile(r"^- (\d*)\s?[:-]+\s?(.*)")
diff_regex = re.compile(r"^(\+- )+(\d*)\s?[:-]+\s?(.*)")
bzs = []
for line in self.diff_output.split("\n"):
match = re.match(regex, line)
match2 = re.match(diff_regex, line)
if match:
bzs.append((match.group(1), match.group(2)))
elif match2:
bzs.append((match2.group(2), match2.group(3)))
return bzs
def _format_lines(self):
output = []
for bz in self.bzs:
output.append("Resolves: #%s - %s" % (bz[0], bz[1]))
if len(output) == 0 and self.required_flags:
# No bugzillas had required flags, use a placeholder if
# we have one, otherwise we have to error out.
if self.placeholder_bz:
print("No bugs with required flags were found, using placeholder: %s" % self.placeholder_bz)
output.append("Related: #%s" % self.placeholder_bz)
else:
error_out("No bugzillas found with required flags: %s" %
self.required_flags)
return output
def _filter_bzs_with_flags(self):
print("Checking flags on bugs: %s" % self.bzs)
print(" required flags: %s" % self.required_flags)
# TODO: Would be nice to load bugs in bulk here but for now we'll
# keep it simple.
filtered_bzs = []
for bz_tuple in self.bzs:
bug_id = bz_tuple[0]
try:
bug = self._load_bug(bug_id)
except xmlrpclib.Fault:
print("WARNING: Bug %s does not seem to exist." % bug_id)
continue
debug("Bug %s has flags: %s" % (bug_id, bug.flags))
flags_missing = False
for flag in self.required_flags:
if bug.get_flag_status(flag[0:-1]) != flag[-1]:
print("WARNING: Bug %s missing required flag: %s" %
(bug_id, flag))
flags_missing = True
break
else:
debug("Bug %s has required flag: %s" %
(bug_id, flag))
if not flags_missing:
filtered_bzs.append(bz_tuple)
return filtered_bzs
def _load_bug(self, bug_id):
bugzilla = RHBugzilla(url='https://bugzilla.redhat.com/xmlrpc.cgi')
return bugzilla.getbug(bug_id, include_fields=['id', 'flags'])

View file

@ -30,10 +30,7 @@ import tempfile
from blessed import Terminal
from bugzilla.rhbugzilla import RHBugzilla
from tito.compat import xmlrpclib, getstatusoutput
from tito.exception import TitoException
from tito.compat import getstatusoutput
from tito.exception import RunCommandException
from tito.tar import TarFixer
@ -89,124 +86,6 @@ def extract_sources(spec_file_lines):
return filenames
class MissingBugzillaCredsException(TitoException):
pass
class BugzillaExtractor(object):
"""
Parses output of a dist-git commit diff looking for changelog
entries that look like they reference bugzilla commits.
Optionally can check bugzilla for required flags on each bug.
"""
def __init__(self, diff_output, required_flags=None,
placeholder_bz=None):
self.diff_output = diff_output
self.required_flags = required_flags
self.placeholder_bz = placeholder_bz
# Tuples of bugzilla ID + commit message we extracted:
self.bzs = []
def extract(self):
self.bzs = self._extract_bzs()
if self.required_flags:
self._check_for_bugzilla_creds()
self.bzs = self._filter_bzs_with_flags()
return self._format_lines()
def _check_for_bugzilla_creds(self):
if not os.path.exists(os.path.expanduser("~/.bugzillarc")):
raise MissingBugzillaCredsException("Missing ~/.bugzillarc")
else:
debug("Found bugzilla credentials in ~/.bugzillarc")
def _extract_bzs(self):
"""
Parses the output of CVS diff or a series of git commit log entries,
looking for new lines which look like a commit of the format:
######: Commit message
Returns a list of lines of text similar to:
Resolves: #XXXXXX - Commit message
If the releaser specifies any required bugzilla flags we will
check each bug found and see if it has all required flags. If not
we skip it. If we end up with *no* bugs with the required flags
our build is likely to fail, so we look for a placeholder bugzilla
defined in relaser config and use that instead if possible, otherwise
error out.
Returns a list of lines to write to the commit message as is.
"""
regex = re.compile(r"^- (\d*)\s?[:-]+\s?(.*)")
diff_regex = re.compile(r"^(\+- )+(\d*)\s?[:-]+\s?(.*)")
bzs = []
for line in self.diff_output.split("\n"):
match = re.match(regex, line)
match2 = re.match(diff_regex, line)
if match:
bzs.append((match.group(1), match.group(2)))
elif match2:
bzs.append((match2.group(2), match2.group(3)))
return bzs
def _format_lines(self):
output = []
for bz in self.bzs:
output.append("Resolves: #%s - %s" % (bz[0], bz[1]))
if len(output) == 0 and self.required_flags:
# No bugzillas had required flags, use a placeholder if
# we have one, otherwise we have to error out.
if self.placeholder_bz:
print("No bugs with required flags were found, using placeholder: %s" % self.placeholder_bz)
output.append("Related: #%s" % self.placeholder_bz)
else:
error_out("No bugzillas found with required flags: %s" %
self.required_flags)
return output
def _filter_bzs_with_flags(self):
print("Checking flags on bugs: %s" % self.bzs)
print(" required flags: %s" % self.required_flags)
# TODO: Would be nice to load bugs in bulk here but for now we'll
# keep it simple.
filtered_bzs = []
for bz_tuple in self.bzs:
bug_id = bz_tuple[0]
try:
bug = self._load_bug(bug_id)
except xmlrpclib.Fault:
print("WARNING: Bug %s does not seem to exist." % bug_id)
continue
debug("Bug %s has flags: %s" % (bug_id, bug.flags))
flags_missing = False
for flag in self.required_flags:
if bug.get_flag_status(flag[0:-1]) != flag[-1]:
print("WARNING: Bug %s missing required flag: %s" %
(bug_id, flag))
flags_missing = True
break
else:
debug("Bug %s has required flag: %s" %
(bug_id, flag))
if not flags_missing:
filtered_bzs.append(bz_tuple)
return filtered_bzs
def _load_bug(self, bug_id):
bugzilla = RHBugzilla(url='https://bugzilla.redhat.com/xmlrpc.cgi')
return bugzilla.getbug(bug_id, include_fields=['id', 'flags'])
def _out(msgs, prefix, color_func, stream=sys.stdout):
if prefix is None:
fmt = "%(msg)s"

View file

@ -16,13 +16,14 @@ import subprocess
import sys
import tempfile
from tito.common import run_command, BugzillaExtractor, debug, extract_sources, \
MissingBugzillaCredsException, error_out, chdir, warn_out, info_out, find_mead_chain_file
from tito.common import run_command, debug, extract_sources, \
error_out, chdir, warn_out, info_out, find_mead_chain_file
from tito.compat import getoutput, getstatusoutput, write
from tito.release import Releaser
from tito.release.main import PROTECTED_BUILD_SYS_FILES
from tito.buildparser import BuildTargetParser
from tito.exception import RunCommandException
from tito.bugtracker import BugzillaExtractor, MissingBugzillaCredsException
import getpass
from string import Template

View file

@ -16,9 +16,10 @@ import tempfile
import subprocess
import sys
from tito.common import run_command, debug, BugzillaExtractor
from tito.common import run_command, debug
from tito.compat import getoutput, write, getstatusoutput
from tito.release.distgit import FedoraGitReleaser
from tito.bugtracker import BugzillaExtractor
class ObsReleaser(FedoraGitReleaser):

View file

@ -0,0 +1,216 @@
import unittest
from tito.bugtracker import BugzillaExtractor
from mock import Mock, patch
class ExtractBugzillasTest(unittest.TestCase):
def test_single_line(self):
commit_log = "- 123456: Did something interesting."
extractor = BugzillaExtractor(commit_log)
results = extractor.extract()
self.assertEquals(1, len(results))
self.assertEquals("Resolves: #123456 - Did something interesting.",
results[0])
def test_single_with_dash(self):
commit_log = "- 123456 - Did something interesting."
extractor = BugzillaExtractor(commit_log)
results = extractor.extract()
self.assertEquals(1, len(results))
self.assertEquals("Resolves: #123456 - Did something interesting.",
results[0])
def test_single_with_no_spaces(self):
commit_log = "- 123456-Did something interesting."
extractor = BugzillaExtractor(commit_log)
results = extractor.extract()
self.assertEquals(1, len(results))
self.assertEquals("Resolves: #123456 - Did something interesting.",
results[0])
def test_diff_format(self):
commit_log = "+- 123456: Did something interesting."
extractor = BugzillaExtractor(commit_log)
results = extractor.extract()
self.assertEquals(1, len(results))
self.assertEquals("Resolves: #123456 - Did something interesting.",
results[0])
def test_single_line_no_bz(self):
commit_log = "- Did something interesting."
extractor = BugzillaExtractor(commit_log)
results = extractor.extract()
self.assertEquals(0, len(results))
def test_multi_line(self):
commit_log = "- 123456: Did something interesting.\n- Another commit.\n" \
"- 456789: A third commit."
extractor = BugzillaExtractor(commit_log)
results = extractor.extract()
self.assertEquals(2, len(results))
self.assertEquals("Resolves: #123456 - Did something interesting.",
results[0])
self.assertEquals("Resolves: #456789 - A third commit.",
results[1])
def test_single_required_flag_found(self):
extractor = BugzillaExtractor("", required_flags=[
'myos-1.0+', 'pm_ack+'])
bug1 = ('123456', 'Did something interesting.')
extractor._extract_bzs = Mock(return_value=[
bug1])
extractor._check_for_bugzilla_creds = Mock()
extractor._load_bug = Mock(
return_value=MockBug(bug1[0], ['myos-1.0+', 'pm_ack+']))
results = extractor.extract()
self.assertEquals(1, len(extractor.bzs))
self.assertEquals(bug1[0], extractor.bzs[0][0])
self.assertEquals(bug1[1], extractor.bzs[0][1])
self.assertEquals(1, len(results))
self.assertEquals("Resolves: #123456 - Did something interesting.",
results[0])
def test_required_flags_found(self):
extractor = BugzillaExtractor("", required_flags=[
'myos-1.0+', 'pm_ack+'])
bug1 = ('123456', 'Did something interesting.')
bug2 = ('444555', 'Something else.')
bug3 = ('987654', 'Such amaze!')
extractor._extract_bzs = Mock(return_value=[
bug1, bug2, bug3])
extractor._check_for_bugzilla_creds = Mock()
bug_mocks = [
MockBug(bug1[0], ['myos-1.0+', 'pm_ack+']),
MockBug(bug2[0], ['myos-2.0?', 'pm_ack?']),
MockBug(bug3[0], ['myos-1.0+', 'pm_ack+'])]
def next_bug(*args):
return bug_mocks.pop(0)
extractor._load_bug = Mock(side_effect=next_bug)
results = extractor.extract()
self.assertEquals(2, len(extractor.bzs))
self.assertEquals(bug1[0], extractor.bzs[0][0])
self.assertEquals(bug1[1], extractor.bzs[0][1])
self.assertEquals(bug3[0], extractor.bzs[1][0])
self.assertEquals(bug3[1], extractor.bzs[1][1])
self.assertEquals(2, len(results))
self.assertEquals("Resolves: #123456 - Did something interesting.",
results[0])
self.assertEquals("Resolves: #987654 - Such amaze!",
results[1])
@patch("tito.bugtracker.error_out")
def test_required_flags_missing(self, mock_error):
required_flags = ['myos-2.0+']
extractor = BugzillaExtractor("", required_flags)
bug1 = ('123456', 'Did something interesting.')
bug2 = ('444555', 'Something else.')
bug3 = ('987654', 'Such amaze!')
extractor._extract_bzs = Mock(return_value=[
bug1, bug2, bug3])
extractor._check_for_bugzilla_creds = Mock()
bug_mocks = [
MockBug(bug1[0], ['myos-1.0+', 'pm_ack+']),
MockBug(bug2[0], ['myos-2.0?', 'pm_ack?']),
MockBug(bug3[0], ['myos-1.0+', 'pm_ack+'])]
def next_bug(*args):
return bug_mocks.pop(0)
extractor._load_bug = Mock(side_effect=next_bug)
results = extractor.extract()
self.assertEquals(0, len(extractor.bzs))
self.assertEquals(0, len(results))
mock_error.assert_called_once_with("No bugzillas found with required flags: %s" % required_flags)
def test_required_flags_missing_with_placeholder(self):
extractor = BugzillaExtractor("", required_flags=[
'myos-2.0+'], placeholder_bz="54321")
bug1 = ('123456', 'Did something interesting.')
extractor._extract_bzs = Mock(return_value=[
bug1])
extractor._check_for_bugzilla_creds = Mock()
extractor._load_bug = Mock(
return_value=MockBug(bug1[0], ['myos-1.0+', 'pm_ack+']))
results = extractor.extract()
self.assertEquals(0, len(extractor.bzs))
self.assertEquals(1, len(results))
self.assertEquals("Related: #54321", results[0])
def test_same_id_multiple_times(self):
extractor = BugzillaExtractor("", required_flags=[
'myos-1.0+', 'pm_ack+'])
bug1 = ('123456', 'Did something interesting.')
bug3 = ('123456', 'Oops, lets try again.')
extractor._extract_bzs = Mock(return_value=[
bug1, bug3])
extractor._check_for_bugzilla_creds = Mock()
extractor._load_bug = Mock(
return_value=MockBug(bug1[0], ['myos-1.0+', 'pm_ack+']))
results = extractor.extract()
self.assertEquals(2, len(extractor.bzs))
self.assertEquals(bug1[0], extractor.bzs[0][0])
self.assertEquals(bug1[1], extractor.bzs[0][1])
self.assertEquals(bug3[0], extractor.bzs[1][0])
self.assertEquals(bug3[1], extractor.bzs[1][1])
self.assertEquals(2, len(results))
self.assertEquals("Resolves: #123456 - Did something interesting.",
results[0])
self.assertEquals("Resolves: #123456 - Oops, lets try again.",
results[1])
@patch("tito.bugtracker.error_out")
def test_bug_doesnt_exist(self, mock_error):
required_flags = ['myos-1.0+', 'pm_ack+']
extractor = BugzillaExtractor("", required_flags)
bug1 = ('123456', 'Did something interesting.')
extractor._extract_bzs = Mock(return_value=[
bug1])
extractor._check_for_bugzilla_creds = Mock()
from tito.compat import xmlrpclib
extractor._load_bug = Mock(side_effect=xmlrpclib.Fault("", ""))
results = extractor.extract()
self.assertEquals(0, len(extractor.bzs))
self.assertEquals(0, len(results))
mock_error.assert_called_once_with("No bugzillas found with required flags: %s" % required_flags)
class MockBug(object):
def __init__(self, bug_id, flags):
self.flags = {}
for flag in flags:
self.flags[flag[0:-1]] = flag[-1]
def get_flag_status(self, flag):
if flag in self.flags:
return self.flags[flag]
else:
return None

View file

@ -16,7 +16,7 @@
from tito.common import (replace_version, find_spec_like_file, increase_version,
search_for, compare_version, run_command_print, find_wrote_in_rpmbuild_output,
render_cheetah, increase_zstream, reset_release, find_file_with_extension,
normalize_class_name, extract_sha1, BugzillaExtractor, DEFAULT_BUILD_DIR, munge_specfile,
normalize_class_name, extract_sha1, DEFAULT_BUILD_DIR, munge_specfile,
munge_setup_macro, get_project_name,
_out)
@ -496,219 +496,6 @@ class VersionMathTest(unittest.TestCase):
self.assertEquals(expected, reset_release(line))
class ExtractBugzillasTest(unittest.TestCase):
def test_single_line(self):
commit_log = "- 123456: Did something interesting."
extractor = BugzillaExtractor(commit_log)
results = extractor.extract()
self.assertEquals(1, len(results))
self.assertEquals("Resolves: #123456 - Did something interesting.",
results[0])
def test_single_with_dash(self):
commit_log = "- 123456 - Did something interesting."
extractor = BugzillaExtractor(commit_log)
results = extractor.extract()
self.assertEquals(1, len(results))
self.assertEquals("Resolves: #123456 - Did something interesting.",
results[0])
def test_single_with_no_spaces(self):
commit_log = "- 123456-Did something interesting."
extractor = BugzillaExtractor(commit_log)
results = extractor.extract()
self.assertEquals(1, len(results))
self.assertEquals("Resolves: #123456 - Did something interesting.",
results[0])
def test_diff_format(self):
commit_log = "+- 123456: Did something interesting."
extractor = BugzillaExtractor(commit_log)
results = extractor.extract()
self.assertEquals(1, len(results))
self.assertEquals("Resolves: #123456 - Did something interesting.",
results[0])
def test_single_line_no_bz(self):
commit_log = "- Did something interesting."
extractor = BugzillaExtractor(commit_log)
results = extractor.extract()
self.assertEquals(0, len(results))
def test_multi_line(self):
commit_log = "- 123456: Did something interesting.\n- Another commit.\n" \
"- 456789: A third commit."
extractor = BugzillaExtractor(commit_log)
results = extractor.extract()
self.assertEquals(2, len(results))
self.assertEquals("Resolves: #123456 - Did something interesting.",
results[0])
self.assertEquals("Resolves: #456789 - A third commit.",
results[1])
def test_single_required_flag_found(self):
extractor = BugzillaExtractor("", required_flags=[
'myos-1.0+', 'pm_ack+'])
bug1 = ('123456', 'Did something interesting.')
extractor._extract_bzs = Mock(return_value=[
bug1])
extractor._check_for_bugzilla_creds = Mock()
extractor._load_bug = Mock(
return_value=MockBug(bug1[0], ['myos-1.0+', 'pm_ack+']))
results = extractor.extract()
self.assertEquals(1, len(extractor.bzs))
self.assertEquals(bug1[0], extractor.bzs[0][0])
self.assertEquals(bug1[1], extractor.bzs[0][1])
self.assertEquals(1, len(results))
self.assertEquals("Resolves: #123456 - Did something interesting.",
results[0])
def test_required_flags_found(self):
extractor = BugzillaExtractor("", required_flags=[
'myos-1.0+', 'pm_ack+'])
bug1 = ('123456', 'Did something interesting.')
bug2 = ('444555', 'Something else.')
bug3 = ('987654', 'Such amaze!')
extractor._extract_bzs = Mock(return_value=[
bug1, bug2, bug3])
extractor._check_for_bugzilla_creds = Mock()
bug_mocks = [
MockBug(bug1[0], ['myos-1.0+', 'pm_ack+']),
MockBug(bug2[0], ['myos-2.0?', 'pm_ack?']),
MockBug(bug3[0], ['myos-1.0+', 'pm_ack+'])]
def next_bug(*args):
return bug_mocks.pop(0)
extractor._load_bug = Mock(side_effect=next_bug)
results = extractor.extract()
self.assertEquals(2, len(extractor.bzs))
self.assertEquals(bug1[0], extractor.bzs[0][0])
self.assertEquals(bug1[1], extractor.bzs[0][1])
self.assertEquals(bug3[0], extractor.bzs[1][0])
self.assertEquals(bug3[1], extractor.bzs[1][1])
self.assertEquals(2, len(results))
self.assertEquals("Resolves: #123456 - Did something interesting.",
results[0])
self.assertEquals("Resolves: #987654 - Such amaze!",
results[1])
@patch("tito.common.error_out")
def test_required_flags_missing(self, mock_error):
required_flags = ['myos-2.0+']
extractor = BugzillaExtractor("", required_flags)
bug1 = ('123456', 'Did something interesting.')
bug2 = ('444555', 'Something else.')
bug3 = ('987654', 'Such amaze!')
extractor._extract_bzs = Mock(return_value=[
bug1, bug2, bug3])
extractor._check_for_bugzilla_creds = Mock()
bug_mocks = [
MockBug(bug1[0], ['myos-1.0+', 'pm_ack+']),
MockBug(bug2[0], ['myos-2.0?', 'pm_ack?']),
MockBug(bug3[0], ['myos-1.0+', 'pm_ack+'])]
def next_bug(*args):
return bug_mocks.pop(0)
extractor._load_bug = Mock(side_effect=next_bug)
results = extractor.extract()
self.assertEquals(0, len(extractor.bzs))
self.assertEquals(0, len(results))
mock_error.assert_called_once_with("No bugzillas found with required flags: %s" % required_flags)
def test_required_flags_missing_with_placeholder(self):
extractor = BugzillaExtractor("", required_flags=[
'myos-2.0+'], placeholder_bz="54321")
bug1 = ('123456', 'Did something interesting.')
extractor._extract_bzs = Mock(return_value=[
bug1])
extractor._check_for_bugzilla_creds = Mock()
extractor._load_bug = Mock(
return_value=MockBug(bug1[0], ['myos-1.0+', 'pm_ack+']))
results = extractor.extract()
self.assertEquals(0, len(extractor.bzs))
self.assertEquals(1, len(results))
self.assertEquals("Related: #54321", results[0])
def test_same_id_multiple_times(self):
extractor = BugzillaExtractor("", required_flags=[
'myos-1.0+', 'pm_ack+'])
bug1 = ('123456', 'Did something interesting.')
bug3 = ('123456', 'Oops, lets try again.')
extractor._extract_bzs = Mock(return_value=[
bug1, bug3])
extractor._check_for_bugzilla_creds = Mock()
extractor._load_bug = Mock(
return_value=MockBug(bug1[0], ['myos-1.0+', 'pm_ack+']))
results = extractor.extract()
self.assertEquals(2, len(extractor.bzs))
self.assertEquals(bug1[0], extractor.bzs[0][0])
self.assertEquals(bug1[1], extractor.bzs[0][1])
self.assertEquals(bug3[0], extractor.bzs[1][0])
self.assertEquals(bug3[1], extractor.bzs[1][1])
self.assertEquals(2, len(results))
self.assertEquals("Resolves: #123456 - Did something interesting.",
results[0])
self.assertEquals("Resolves: #123456 - Oops, lets try again.",
results[1])
@patch("tito.common.error_out")
def test_bug_doesnt_exist(self, mock_error):
required_flags = ['myos-1.0+', 'pm_ack+']
extractor = BugzillaExtractor("", required_flags)
bug1 = ('123456', 'Did something interesting.')
extractor._extract_bzs = Mock(return_value=[
bug1])
extractor._check_for_bugzilla_creds = Mock()
from tito.compat import xmlrpclib
extractor._load_bug = Mock(side_effect=xmlrpclib.Fault("", ""))
results = extractor.extract()
self.assertEquals(0, len(extractor.bzs))
self.assertEquals(0, len(results))
mock_error.assert_called_once_with("No bugzillas found with required flags: %s" % required_flags)
class MockBug(object):
def __init__(self, bug_id, flags):
self.flags = {}
for flag in flags:
self.flags[flag[0:-1]] = flag[-1]
def get_flag_status(self, flag):
if flag in self.flags:
return self.flags[flag]
else:
return None
class MungeSetupMacroTests(unittest.TestCase):
SOURCE = "tito-git-3.20362dd"