Configuration system uses a class, add test suite

This commit is contained in:
Stefano Rivera 2010-12-20 22:08:12 +02:00
parent 9c62fb8723
commit 254be7bb5d
7 changed files with 246 additions and 128 deletions

View File

@ -30,7 +30,7 @@ from debian.deb822 import Dsc
import launchpadlib.launchpad
import lsb_release
from ubuntutools.config import get_value, ubu_email
from ubuntutools.config import UDTConfig, ubu_email
from ubuntutools.builder import getBuilder
from ubuntutools.logger import Logger
from ubuntutools.question import YesNoQuestion
@ -71,12 +71,12 @@ def parse(args):
help='Build the package before uploading (default: %default)')
p.add_option('-B', '--builder',
dest='builder',
default=get_value('BUILDER'),
help='Specify the package builder (default: %default)',
default=None,
help='Specify the package builder (default: pbuilder)',
metavar='BUILDER')
p.add_option('-U', '--update',
dest='update',
default=get_value('UPDATE_BUILDER'),
default=False,
action='store_true',
help='Update the build environment before attempting to build')
p.add_option('-u', '--upload',
@ -95,23 +95,32 @@ def parse(args):
metavar='VERSION')
p.add_option('-w', '--workdir',
dest='workdir',
default=get_value('WORKDIR'),
default=None,
help='Specify a working directory (default: temporary dir)',
metavar='WORKDIR')
p.add_option('-l', '--launchpad',
dest='launchpad',
default=get_value('LPINSTANCE'),
help='Launchpad instance to connect to (default: %default)',
default=None,
help='Launchpad instance to connect to (default: production)',
metavar='INSTANCE')
p.add_option('--no-conf', '--noconf',
dest='no_configuration',
dest='no_conf',
default=False,
help="Don't read config files, must be the first option given",
help="Don't read config files or environment variables",
action='store_true')
opts, args = p.parse_args(args)
if len(args) != 1:
p.error('You must specify a single source package or a .dsc URL/path.')
config = UDTConfig(opts.no_conf)
if opts.builder is None:
opts.builder = config.get_value('BUILDER')
if not opts.update:
opts.update = config.get_value('UPDATE_BUILDER')
if opts.workdir is None:
opts.workdir = config.get_value('WORKDIR')
if opts.launchpad is None:
opts.launchpad = config.get_value('LPINSTANCE')
if not opts.upload and not opts.workdir:
p.error('Please specify either a working dir or an upload target!')
@ -239,7 +248,6 @@ def do_backport(workdir, package, dscfile, version, suffix, release, build,
bp_version = get_backport_version(version, suffix, upload, release)
bp_dist = get_backport_dist(upload, release)
ubu_email()
check_call(['dch',
'--force-bad-version',
'--preserve',
@ -262,6 +270,7 @@ def do_backport(workdir, package, dscfile, version, suffix, release, build,
def main(args):
os.environ['DEB_VENDOR'] = 'Ubuntu'
ubu_email()
opts, (package_or_dsc,) = parse(args[1:])

1
debian/copyright vendored
View File

@ -189,7 +189,6 @@ Files:
sponsor-patch,
suspicious-source,
ubuntutools/builder.py,
ubuntutools/common.py,
ubuntutools/config.py,
ubuntutools/logger.py,
ubuntutools/question.py,

View File

@ -22,7 +22,6 @@
import os
import subprocess
from ubuntutools.config import get_value
from ubuntutools.logger import Logger
class Builder(object):
@ -145,10 +144,7 @@ class Sbuild(Builder):
return 0
def getBuilder(builder=None):
if not builder:
builder = get_value('BUILDER')
def getBuilder(builder='pbuilder'):
if builder == 'pbuilder':
return Pbuilder()
elif builder == 'pbuilder-dist':

View File

@ -1,25 +0,0 @@
# common.py - provides functions which are commonly used by the
# ubuntu-dev-tools package.
#
# Copyright (C) 2010, Stefano Rivera <stefanor@ubuntu.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
def memoize_noargs(func):
"Simple memoization wrapper, for functions without arguments"
func.cache = None
def wrapper():
if func.cache is None:
func.cache = func()
return func.cache
return wrapper

View File

@ -22,65 +22,70 @@ import re
import socket
import sys
from ubuntutools.common import memoize_noargs
class UDTConfig(object):
defaults = {
'BUILDER': 'pbuilder',
'UPDATE_BUILDER': False,
'LPINSTANCE': 'production',
}
defaults = {
'BUILDER': 'pbuilder',
'UPDATE_BUILDER': False,
'LPINSTANCE': 'production',
}
@memoize_noargs
def get_devscripts_config():
"""Read the devscripts configuration files, and return the values as a
dictionary
"""
config = {}
var_re = re.compile(r'^\s*([A-Z_]+?)=(.+?)\s*$')
for fn in ('/etc/devscripts.conf', '~/.devscripts'):
f = open(os.path.expanduser(fn), 'r')
for line in f:
m = var_re.match(line)
if m:
value = m.group(2)
# This isn't quite the same as bash's parsing, but
# mostly-compatible for configuration files that aren't broken
# like this: KEY=foo bar
if (len(value) > 2 and value[0] == value[-1]
and value[0] in ("'", '"')):
value = value[1:-1]
config[m.group(1)] = value
f.close()
return config
def __init__(self, no_conf=False, prefix=None):
def get_value(key, default=None, prefix=None, compat_keys=[]):
"""Retrieve a value from the environment or configuration files.
keys are prefixed with the script name + _, or prefix.
self.no_conf = no_conf
if prefix is None:
prefix = os.path.basename(sys.argv[0]).upper().replace('-', '_')
self.prefix = prefix
if not no_conf:
self.config = self.parse_devscripts_config()
Store Priority: Environment variables, user config file, system config file
Variable Priority: PREFIX_KEY, UBUNTUTOOLS_KEY, compat_keys
def parse_devscripts_config(self):
"""Read the devscripts configuration files, and return the values as a
dictionary
"""
config = {}
var_re = re.compile(r'^\s*([A-Z_]+?)=(.+?)\s*$')
for fn in ('/etc/devscripts.conf', '~/.devscripts'):
f = open(os.path.expanduser(fn), 'r')
for line in f:
m = var_re.match(line)
if m:
value = m.group(2)
# This isn't quite the same as bash's parsing, but
# mostly-compatible for configuration files that aren't
# broken like this: KEY=foo bar
if (len(value) > 2 and value[0] == value[-1]
and value[0] in ("'", '"')):
value = value[1:-1]
config[m.group(1)] = value
f.close()
return config
Historical variable names can be supplied via compat_keys, no prefix is
applied to them.
"""
if default is None and key in defaults:
default = defaults[key]
if len(sys.argv) > 1 and sys.argv[1] in ('--no-conf', '--noconf'):
def get_value(self, key, default=None, compat_keys=[]):
"""Retrieve a value from the environment or configuration files.
keys are prefixed with the script name, falling back to UBUNTUTOOLS for
package-wide keys.
Store Priority: Environment variables, user conf, system conf
Variable Priority: PREFIX_KEY, UBUNTUTOOLS_KEY, compat_keys
Historical variable names can be supplied via compat_keys, no prefix is
applied to them.
"""
if default is None and key in self.defaults:
default = self.defaults[key]
keys = [self.prefix + '_' + key, 'UBUNTUTOOLS_' + key] + compat_keys
for store in (os.environ, self.config):
for k in keys:
if k in store:
value = store[k]
if value in ('yes', 'no'):
value = value == 'yes'
return value
return default
if prefix is None:
prefix = os.path.basename(sys.argv[0]).upper().replace('-', '_') + '_'
keys = [prefix + key, 'UBUNTUTOOLS_' + key] + compat_keys
for store in (os.environ, get_devscripts_config()):
for k in keys:
if k in store:
value = store[k]
if value in ('yes', 'no'):
value = value == 'yes'
return value
return default
def ubu_email(name=None, email=None, export=True):
"""Find the developer's Ubuntu e-mail address, and export it in

View File

@ -1,35 +0,0 @@
# test_common.py - Test suite for ubuntutools.common
#
# Copyright (C) 2010, Stefano Rivera <stefanor@ubuntu.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
from ubuntutools.test import unittest
from ubuntutools.common import memoize_noargs
class MemoizeTestCase(unittest.TestCase):
def test_memoize_noargs(self):
global run_count
run_count = 0
@memoize_noargs
def test_func():
global run_count
run_count += 1
return 42
self.assertEqual(run_count, 0)
self.assertEqual(test_func(), 42)
self.assertEqual(run_count, 1)
self.assertEqual(test_func(), 42)
self.assertEqual(run_count, 1)

View File

@ -0,0 +1,169 @@
# test_config.py - Test suite for ubuntutools.config
#
# Copyright (C) 2010, Stefano Rivera <stefanor@ubuntu.com>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
import os
import os.path
from StringIO import StringIO
import ubuntutools.config
from ubuntutools.config import UDTConfig, ubu_email
from ubuntutools.test import unittest
config_files = {
'system': '',
'user': '',
}
def fake_open(filename, mode='r'):
if mode != 'r':
raise IOError("Read only fake-file")
files = {
'/etc/devscripts.conf': config_files['system'],
os.path.expanduser('~/.devscripts'): config_files['user'],
}
if filename not in files:
raise IOError("No such file or directory: '%s'" % filename)
return StringIO(files[filename])
class ConfigTestCase(unittest.TestCase):
def setUp(self):
ubuntutools.config.open = fake_open
self.cleanEnvironment()
def tearDown(self):
del ubuntutools.config.open
self.cleanEnvironment()
def cleanEnvironment(self):
config_files['system'] = ''
config_files['user'] = ''
for k in os.environ.keys():
if k.startswith(('UBUNTUTOOLS_', 'TEST_')):
del os.environ[k]
def test_config_parsing(self):
config_files['user'] = """#COMMENT=yes
\tTAB_INDENTED=yes
SPACE_INDENTED=yes
SPACE_SUFFIX=yes
SINGLE_QUOTE='yes no'
DOUBLE_QUOTE="yes no"
QUOTED_QUOTE="it's"
INHERIT=user
REPEAT=no
REPEAT=yes
"""
config_files['system'] = 'INHERIT=system'
self.assertEqual(UDTConfig(prefix='TEST').config, {
'TAB_INDENTED': 'yes',
'SPACE_INDENTED': 'yes',
'SPACE_SUFFIX': 'yes',
'SINGLE_QUOTE': 'yes no',
'DOUBLE_QUOTE': 'yes no',
'QUOTED_QUOTE': "it's",
'INHERIT': 'user',
'REPEAT': 'yes',
})
def get_value(self, key, default=None, compat_keys=[]):
config = UDTConfig(prefix='TEST')
return config.get_value(key, default=default, compat_keys=compat_keys)
def test_defaults(self):
self.assertEqual(self.get_value('BUILDER'), 'pbuilder')
def test_provided_default(self):
self.assertEqual(self.get_value('BUILDER', default='foo'), 'foo')
def test_scriptname_precedence(self):
config_files['user'] = """TEST_BUILDER=foo
UBUNTUTOOLS_BUILDER=bar"""
self.assertEqual(self.get_value('BUILDER'), 'foo')
def test_configfile_precedence(self):
config_files['system'] = "UBUNTUTOOLS_BUILDER=foo"
config_files['user'] = "UBUNTUTOOLS_BUILDER=bar"
self.assertEqual(self.get_value('BUILDER'), 'bar')
def test_environment_precedence(self):
config_files['user'] = "UBUNTUTOOLS_BUILDER=bar"
os.environ['UBUNTUTOOLS_BUILDER'] = 'baz'
self.assertEqual(self.get_value('BUILDER'), 'baz')
def test_any_environment_precedence(self):
config_files['user'] = "TEST_BUILDER=bar"
os.environ['UBUNTUTOOLS_BUILDER'] = 'foo'
self.assertEqual(self.get_value('BUILDER'), 'foo')
def test_compat_environment_precedence(self):
config_files['user'] = "TEST_BUILDER=bar"
os.environ['BUILDER'] = 'baz'
self.assertEqual(self.get_value('BUILDER', compat_keys=['BUILDER']),
'baz')
def test_boolean(self):
config_files['user'] = "TEST_BOOLEAN=yes"
self.assertEqual(self.get_value('BOOLEAN'), True)
config_files['user'] = "TEST_BOOLEAN=no"
self.assertEqual(self.get_value('BOOLEAN'), False)
class UbuEmailTestCase(unittest.TestCase):
def setUp(self):
self.cleanEnvironment()
def tearDown(self):
self.cleanEnvironment()
def cleanEnvironment(self):
for k in ('UBUMAIL', 'DEBEMAIL', 'DEBFULLNAME'):
if k in os.environ:
del os.environ[k]
def test_pristine(self):
os.environ['DEBFULLNAME'] = name = 'Joe Developer'
os.environ['DEBEMAIL'] = email = 'joe@example.net'
self.assertEqual(ubu_email(), (name, email))
def test_two_hat(self):
os.environ['DEBFULLNAME'] = name = 'Joe Developer'
os.environ['DEBEMAIL'] = 'joe@debian.org'
os.environ['UBUMAIL'] = email = 'joe@ubuntu.com'
self.assertEqual(ubu_email(), (name, email))
self.assertEqual(os.environ['DEBFULLNAME'], name)
self.assertEqual(os.environ['DEBEMAIL'], email)
def test_two_hat_with_name(self):
os.environ['DEBFULLNAME'] = 'Joe Developer'
os.environ['DEBEMAIL'] = 'joe@debian.org'
name = 'Joe Ubuntunista'
email = 'joe@ubuntu.com'
os.environ['UBUMAIL'] = '%s <%s>' % (name, email)
self.assertEqual(ubu_email(), (name, email))
self.assertEqual(os.environ['DEBFULLNAME'], name)
self.assertEqual(os.environ['DEBEMAIL'], email)
def test_debfullname_with_email(self):
name = 'Joe Developer'
email = 'joe@example.net'
os.environ['DEBFULLNAME'] = '%s <%s>' % (name, email)
self.assertEqual(ubu_email(), (name, email))
def test_debemail_with_name(self):
name = 'Joe Developer'
email = 'joe@example.net'
os.environ['DEBEMAIL'] = '%s <%s>' % (name, email)
self.assertEqual(ubu_email(), (name, email))