Upgrade pbr to 0.10.0

This commit is contained in:
Matthew Jones
2014-08-06 15:34:20 -04:00
parent 7cb892a869
commit e1da8d169f
7 changed files with 245 additions and 172 deletions

View File

@@ -38,7 +38,7 @@ ordereddict==1.1 (ordereddict.py, needed for Python 2.6 support)
os-diskconfig-python-novaclient-ext==0.1.2 (os_diskconfig_python_novaclient_ext/*) os-diskconfig-python-novaclient-ext==0.1.2 (os_diskconfig_python_novaclient_ext/*)
os-networksv2-python-novaclient-ext==0.21 (os_networksv2_python_novaclient_ext.py) os-networksv2-python-novaclient-ext==0.21 (os_networksv2_python_novaclient_ext.py)
os-virtual-interfacesv2-python-novaclient-ext==0.15 (os_virtual_interfacesv2_python_novaclient_ext.py) os-virtual-interfacesv2-python-novaclient-ext==0.15 (os_virtual_interfacesv2_python_novaclient_ext.py)
pbr==0.8.0 (pbr/*) pbr==0.10.0 (pbr/*)
pexpect==3.3 (pexpect/*, excluded pxssh.py, fdpexpect.py, FSM.py, screen.py, pexpect==3.3 (pexpect/*, excluded pxssh.py, fdpexpect.py, FSM.py, screen.py,
ANSI.py) ANSI.py)
pip==1.5.4 (pip/*, excluded bin/pip*) pip==1.5.4 (pip/*, excluded bin/pip*)

View File

@@ -40,6 +40,7 @@
from distutils import core from distutils import core
from distutils import errors from distutils import errors
import logging
import os import os
import sys import sys
import warnings import warnings
@@ -49,7 +50,17 @@ from setuptools import dist
from pbr import util from pbr import util
_saved_core_distribution = core.Distribution
def _monkeypatch_distribution():
core.Distribution = dist._get_unpatched(core.Distribution) core.Distribution = dist._get_unpatched(core.Distribution)
def _restore_distribution_monkeypatch():
core.Distribution = _saved_core_distribution
if sys.version_info[0] == 3: if sys.version_info[0] == 3:
string_type = str string_type = str
integer_types = (int,) integer_types = (int,)
@@ -76,6 +87,8 @@ def pbr(dist, attr, value):
not work well with distributions that do use a `Distribution` subclass. not work well with distributions that do use a `Distribution` subclass.
""" """
try:
_monkeypatch_distribution()
if not value: if not value:
return return
if isinstance(value, string_type): if isinstance(value, string_type):
@@ -91,14 +104,18 @@ def pbr(dist, attr, value):
attrs = util.cfg_to_args(path) attrs = util.cfg_to_args(path)
except Exception: except Exception:
e = sys.exc_info()[1] e = sys.exc_info()[1]
# NB: This will output to the console if no explicit logging has
# been setup - but thats fine, this is a fatal distutils error, so
# being pretty isn't the #1 goal.. being diagnosable is.
logging.exception('Error parsing')
raise errors.DistutilsSetupError( raise errors.DistutilsSetupError(
'Error parsing %s: %s: %s' % (path, e.__class__.__name__, e)) 'Error parsing %s: %s: %s' % (path, e.__class__.__name__, e))
# Repeat some of the Distribution initialization code with the newly # Repeat some of the Distribution initialization code with the newly
# provided attrs # provided attrs
if attrs: if attrs:
# Skips 'options' and 'licence' support which are rarely used; may add # Skips 'options' and 'licence' support which are rarely used; may
# back in later if demanded # add back in later if demanded
for key, val in attrs.items(): for key, val in attrs.items():
if hasattr(dist.metadata, 'set_' + key): if hasattr(dist.metadata, 'set_' + key):
getattr(dist.metadata, 'set_' + key)(val) getattr(dist.metadata, 'set_' + key)(val)
@@ -120,8 +137,13 @@ def pbr(dist, attr, value):
# This bit of hackery is necessary so that the Distribution will ignore # This bit of hackery is necessary so that the Distribution will ignore
# normally unsupport command options (namely pre-hooks and post-hooks). # normally unsupport command options (namely pre-hooks and post-hooks).
# dist.command_options is normally a dict mapping command names to dicts of # dist.command_options is normally a dict mapping command names to
# their options. Now it will be a defaultdict that returns IgnoreDicts for # dicts of their options. Now it will be a defaultdict that returns
# the each command's options so we can pass through the unsupported options # IgnoreDicts for the each command's options so we can pass through the
# unsupported options
ignore = ['pre_hook.*', 'post_hook.*'] ignore = ['pre_hook.*', 'post_hook.*']
dist.command_options = util.DefaultGetDict(lambda: util.IgnoreDict(ignore)) dist.command_options = util.DefaultGetDict(
lambda: util.IgnoreDict(ignore)
)
finally:
_restore_distribution_monkeypatch()

View File

@@ -49,9 +49,6 @@ from pbr import extra_files
TRUE_VALUES = ('true', '1', 'yes') TRUE_VALUES = ('true', '1', 'yes')
REQUIREMENTS_FILES = ('requirements.txt', 'tools/pip-requires') REQUIREMENTS_FILES = ('requirements.txt', 'tools/pip-requires')
TEST_REQUIREMENTS_FILES = ('test-requirements.txt', 'tools/test-requires') TEST_REQUIREMENTS_FILES = ('test-requirements.txt', 'tools/test-requires')
# part of the standard library starting with 2.7
# adding it to the requirements list screws distro installs
BROKEN_ON_27 = ('argparse', 'importlib', 'ordereddict')
def get_requirements_files(): def get_requirements_files():
@@ -125,6 +122,13 @@ def parse_requirements(requirements_files=None):
if (not line.strip()) or line.startswith('#'): if (not line.strip()) or line.startswith('#'):
continue continue
# Handle nested requirements files such as:
# -r other-requirements.txt
if line.startswith('-r'):
req_file = line.partition(' ')[2]
requirements += parse_requirements([req_file])
continue
try: try:
project_name = pkg_resources.Requirement.parse(line).project_name project_name = pkg_resources.Requirement.parse(line).project_name
except ValueError: except ValueError:
@@ -146,10 +150,6 @@ def parse_requirements(requirements_files=None):
elif re.match(r'\s*-f\s+', line): elif re.match(r'\s*-f\s+', line):
line = None line = None
reason = 'Index Location' reason = 'Index Location'
elif (project_name and
project_name in BROKEN_ON_27 and sys.version_info >= (2, 7)):
line = None
reason = 'Python 2.6 only dependency'
if line is not None: if line is not None:
requirements.append(line) requirements.append(line)
@@ -246,7 +246,9 @@ def write_git_changelog(git_dir=None, dest_dir=os.path.curdir,
"""Write a changelog based on the git changelog.""" """Write a changelog based on the git changelog."""
should_skip = get_boolean_option(option_dict, 'skip_changelog', should_skip = get_boolean_option(option_dict, 'skip_changelog',
'SKIP_WRITE_GIT_CHANGELOG') 'SKIP_WRITE_GIT_CHANGELOG')
if not should_skip: if should_skip:
return
new_changelog = os.path.join(dest_dir, 'ChangeLog') new_changelog = os.path.join(dest_dir, 'ChangeLog')
# If there's already a ChangeLog and it's not writable, just use it # If there's already a ChangeLog and it's not writable, just use it
if (os.path.exists(new_changelog) if (os.path.exists(new_changelog)
@@ -255,7 +257,9 @@ def write_git_changelog(git_dir=None, dest_dir=os.path.curdir,
log.info('[pbr] Writing ChangeLog') log.info('[pbr] Writing ChangeLog')
if git_dir is None: if git_dir is None:
git_dir = _get_git_directory() git_dir = _get_git_directory()
if git_dir: if not git_dir:
return
log_cmd = ['log', '--oneline', '--decorate'] log_cmd = ['log', '--oneline', '--decorate']
changelog = _run_git_command(log_cmd, git_dir) changelog = _run_git_command(log_cmd, git_dir)
first_line = True first_line = True
@@ -300,7 +304,9 @@ def generate_authors(git_dir=None, dest_dir='.', option_dict=dict()):
"""Create AUTHORS file using git commits.""" """Create AUTHORS file using git commits."""
should_skip = get_boolean_option(option_dict, 'skip_authors', should_skip = get_boolean_option(option_dict, 'skip_authors',
'SKIP_GENERATE_AUTHORS') 'SKIP_GENERATE_AUTHORS')
if not should_skip: if should_skip:
return
old_authors = os.path.join(dest_dir, 'AUTHORS.in') old_authors = os.path.join(dest_dir, 'AUTHORS.in')
new_authors = os.path.join(dest_dir, 'AUTHORS') new_authors = os.path.join(dest_dir, 'AUTHORS')
# If there's already an AUTHORS file and it's not writable, just use it # If there's already an AUTHORS file and it's not writable, just use it
@@ -315,7 +321,7 @@ def generate_authors(git_dir=None, dest_dir='.', option_dict=dict()):
authors = [] authors = []
# don't include jenkins email address in AUTHORS file # don't include jenkins email address in AUTHORS file
git_log_cmd = ['log', '--use-mailmap', '--format=%aN <%aE>'] git_log_cmd = ['log', '--format=%aN <%aE>']
authors += _run_git_command(git_log_cmd, git_dir).split('\n') authors += _run_git_command(git_log_cmd, git_dir).split('\n')
authors = [a for a in authors if not re.search(ignore_emails, a)] authors = [a for a in authors if not re.search(ignore_emails, a)]
@@ -794,7 +800,7 @@ def _get_version_from_git(pre_version):
""" """
git_dir = _get_git_directory() git_dir = _get_git_directory()
if git_dir: if git_dir and _git_is_installed():
if pre_version: if pre_version:
try: try:
return _run_git_command( return _run_git_command(

View File

@@ -40,11 +40,14 @@ package metadata.
from distutils import cmd from distutils import cmd
import distutils.errors import distutils.errors
import logging
import os import os
import sys import sys
from testrepository import commands from testrepository import commands
logger = logging.getLogger(__name__)
class Testr(cmd.Command): class Testr(cmd.Command):
@@ -54,14 +57,17 @@ class Testr(cmd.Command):
('coverage', None, "Replace PYTHON with coverage and merge coverage " ('coverage', None, "Replace PYTHON with coverage and merge coverage "
"from each testr worker."), "from each testr worker."),
('testr-args=', 't', "Run 'testr' with these args"), ('testr-args=', 't', "Run 'testr' with these args"),
('omit=', 'o', 'Files to omit from coverage calculations'), ('omit=', 'o', "Files to omit from coverage calculations"),
('coverage-package-name=', None, "Use this name for coverage package"),
('slowest', None, "Show slowest test times after tests complete."), ('slowest', None, "Show slowest test times after tests complete."),
('no-parallel', None, "Run testr serially"), ('no-parallel', None, "Run testr serially"),
('log-level=', 'l', "Log level (default: info)"),
] ]
boolean_options = ['coverage', 'slowest', 'no_parallel'] boolean_options = ['coverage', 'slowest', 'no_parallel']
def _run_testr(self, *args): def _run_testr(self, *args):
logger.debug("_run_testr called with args = %r", args)
return commands.run_argv([sys.argv[0]] + list(args), return commands.run_argv([sys.argv[0]] + list(args),
sys.stdin, sys.stdout, sys.stderr) sys.stdin, sys.stdout, sys.stderr)
@@ -70,18 +76,28 @@ class Testr(cmd.Command):
self.coverage = None self.coverage = None
self.omit = "" self.omit = ""
self.slowest = None self.slowest = None
self.coverage_package_name = None
self.no_parallel = None self.no_parallel = None
self.log_level = 'info'
def finalize_options(self): def finalize_options(self):
self.log_level = getattr(
logging,
self.log_level.upper(),
logging.INFO)
logging.basicConfig(level=self.log_level)
logger.debug("finalize_options called")
if self.testr_args is None: if self.testr_args is None:
self.testr_args = [] self.testr_args = []
else: else:
self.testr_args = self.testr_args.split() self.testr_args = self.testr_args.split()
if self.omit: if self.omit:
self.omit = "--omit=%s" % self.omit self.omit = "--omit=%s" % self.omit
logger.debug("finalize_options: self.__dict__ = %r", self.__dict__)
def run(self): def run(self):
"""Set up testr repo, then run testr""" """Set up testr repo, then run testr"""
logger.debug("run called")
if not os.path.isdir(".testrepository"): if not os.path.isdir(".testrepository"):
self._run_testr("init") self._run_testr("init")
@@ -101,12 +117,19 @@ class Testr(cmd.Command):
self._coverage_after() self._coverage_after()
def _coverage_before(self): def _coverage_before(self):
logger.debug("_coverage_before called")
package = self.distribution.get_name() package = self.distribution.get_name()
if package.startswith('python-'): if package.startswith('python-'):
package = package[7:] package = package[7:]
# Use this as coverage package name
if self.coverage_package_name:
package = self.coverage_package_name
options = "--source %s --parallel-mode" % package options = "--source %s --parallel-mode" % package
os.environ['PYTHON'] = ("coverage run %s" % options) os.environ['PYTHON'] = ("coverage run %s" % options)
logger.debug("os.environ['PYTHON'] = %r", os.environ['PYTHON'])
def _coverage_after(self): def _coverage_after(self):
logger.debug("_coverage_after called")
os.system("coverage combine") os.system("coverage combine")
os.system("coverage html -d ./cover %s" % self.omit) os.system("coverage html -d ./cover %s" % self.omit)

View File

@@ -92,6 +92,10 @@ class BaseTestCase(testtools.TestCase, testresources.ResourcedTestCase):
self.log_fixture = self.useFixture( self.log_fixture = self.useFixture(
fixtures.FakeLogger('pbr')) fixtures.FakeLogger('pbr'))
# Older git does not have config --local, so create a temporary home
# directory to permit using git config --global without stepping on
# developer configuration.
self.useFixture(fixtures.TempHomeDir())
self.useFixture(fixtures.NestedTempfile()) self.useFixture(fixtures.NestedTempfile())
self.useFixture(fixtures.FakeLogger()) self.useFixture(fixtures.FakeLogger())
self.useFixture(fixtures.EnvironmentVariable('PBR_VERSION', '0.0')) self.useFixture(fixtures.EnvironmentVariable('PBR_VERSION', '0.0'))
@@ -112,17 +116,24 @@ class BaseTestCase(testtools.TestCase, testresources.ResourcedTestCase):
k.startswith('pbr_testpackage.')): k.startswith('pbr_testpackage.')):
del sys.modules[k] del sys.modules[k]
def run_setup(self, *args): def run_setup(self, *args, **kwargs):
return self._run_cmd(sys.executable, ('setup.py',) + args) return self._run_cmd(sys.executable, ('setup.py',) + args, **kwargs)
def _run_cmd(self, cmd, args=[]): def _run_cmd(self, cmd, args=[], allow_fail=True, cwd=None):
"""Run a command in the root of the test working copy. """Run a command in the root of the test working copy.
Runs a command, with the given argument list, in the root of the test Runs a command, with the given argument list, in the root of the test
working copy--returns the stdout and stderr streams and the exit code working copy--returns the stdout and stderr streams and the exit code
from the subprocess. from the subprocess.
:param cwd: If falsy run within the test package dir, otherwise run
within the named path.
""" """
return _run_cmd([cmd] + list(args), cwd=self.package_dir) cwd = cwd or self.package_dir
result = _run_cmd([cmd] + list(args), cwd=cwd)
if result[2] and not allow_fail:
raise Exception("Command failed retcode=%s" % result[2])
return result
def _run_cmd(args, cwd): def _run_cmd(args, cwd):

View File

@@ -39,6 +39,7 @@
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
import os import os
import tempfile
import fixtures import fixtures
import mock import mock
@@ -47,18 +48,37 @@ from pbr import packaging
from pbr.tests import base from pbr.tests import base
class TestRepo(fixtures.Fixture):
"""A git repo for testing with.
Use of TempHomeDir with this fixture is strongly recommended as due to the
lack of config --local in older gits, it will write to the users global
configuration without TempHomeDir.
"""
def __init__(self, basedir):
super(TestRepo, self).__init__()
self._basedir = basedir
def setUp(self):
super(TestRepo, self).setUp()
base._run_cmd(['git', 'init', '.'], self._basedir)
base._run_cmd(
['git', 'config', '--global', 'user.email', 'example@example.com'],
self._basedir)
base._run_cmd(['git', 'add', '.'], self._basedir)
def commit(self):
base._run_cmd(['git', 'commit', '-m', 'test commit'], self._basedir)
class TestPackagingInGitRepoWithCommit(base.BaseTestCase): class TestPackagingInGitRepoWithCommit(base.BaseTestCase):
def setUp(self): def setUp(self):
super(TestPackagingInGitRepoWithCommit, self).setUp() super(TestPackagingInGitRepoWithCommit, self).setUp()
self.useFixture(fixtures.TempHomeDir()) repo = self.useFixture(TestRepo(self.package_dir))
self._run_cmd( repo.commit()
'git', ['config', '--global', 'user.email', 'nobody@example.com']) self.run_setup('sdist', allow_fail=False)
self._run_cmd('git', ['init', '.'])
self._run_cmd('git', ['add', '.'])
self._run_cmd('git', ['commit', '-m', 'test commit'])
self.run_setup('sdist')
return
def test_authors(self): def test_authors(self):
# One commit, something should be in the authors list # One commit, something should be in the authors list
@@ -77,10 +97,8 @@ class TestPackagingInGitRepoWithoutCommit(base.BaseTestCase):
def setUp(self): def setUp(self):
super(TestPackagingInGitRepoWithoutCommit, self).setUp() super(TestPackagingInGitRepoWithoutCommit, self).setUp()
self._run_cmd('git', ['init', '.']) self.useFixture(TestRepo(self.package_dir))
self._run_cmd('git', ['add', '.']) self.run_setup('sdist', allow_fail=False)
self.run_setup('sdist')
return
def test_authors(self): def test_authors(self):
# No commits, no authors in list # No commits, no authors in list
@@ -99,8 +117,7 @@ class TestPackagingInPlainDirectory(base.BaseTestCase):
def setUp(self): def setUp(self):
super(TestPackagingInPlainDirectory, self).setUp() super(TestPackagingInPlainDirectory, self).setUp()
self.run_setup('sdist') self.run_setup('sdist', allow_fail=False)
return
def test_authors(self): def test_authors(self):
# Not a git repo, no AUTHORS file created # Not a git repo, no AUTHORS file created
@@ -126,3 +143,17 @@ class TestPresenceOfGit(base.BaseTestCase):
'_run_shell_command') as _command: '_run_shell_command') as _command:
_command.side_effect = OSError _command.side_effect = OSError
self.assertEqual(False, packaging._git_is_installed()) self.assertEqual(False, packaging._git_is_installed())
class TestNestedRequirements(base.BaseTestCase):
def test_nested_requirement(self):
tempdir = tempfile.mkdtemp()
requirements = os.path.join(tempdir, 'requirements.txt')
nested = os.path.join(tempdir, 'nested.txt')
with open(requirements, 'w') as f:
f.write('-r ' + nested)
with open(nested, 'w') as f:
f.write('pbr')
result = packaging.parse_requirements([requirements])
self.assertEqual(result, ['pbr'])

View File

@@ -151,7 +151,7 @@ class GitLogsTest(base.BaseTestCase):
co_author_by = u"Co-authored-by: " + co_author co_author_by = u"Co-authored-by: " + co_author
git_log_cmd = ( git_log_cmd = (
"git --git-dir=%s log --use-mailmap --format=%%aN <%%aE>" "git --git-dir=%s log --format=%%aN <%%aE>"
% self.git_dir) % self.git_dir)
git_co_log_cmd = ("git --git-dir=%s log" % self.git_dir) git_co_log_cmd = ("git --git-dir=%s log" % self.git_dir)
git_top_level = "git rev-parse --show-toplevel" git_top_level = "git rev-parse --show-toplevel"
@@ -304,26 +304,6 @@ class ParseRequirementsTest(base.BaseTestCase):
fh.write("-f foobar") fh.write("-f foobar")
self.assertEqual([], packaging.parse_requirements([self.tmp_file])) self.assertEqual([], packaging.parse_requirements([self.tmp_file]))
def test_parse_requirements_removes_argparse(self):
with open(self.tmp_file, 'w') as fh:
fh.write("argparse")
if sys.version_info >= (2, 7):
self.assertEqual([], packaging.parse_requirements([self.tmp_file]))
def test_parse_requirements_removes_versioned_ordereddict(self):
self.useFixture(fixtures.MonkeyPatch('sys.version_info', (2, 7)))
with open(self.tmp_file, 'w') as fh:
fh.write("ordereddict==1.0.1")
self.assertEqual([], packaging.parse_requirements([self.tmp_file]))
def test_parse_requirements_keeps_versioned_ordereddict(self):
self.useFixture(fixtures.MonkeyPatch('sys.version_info', (2, 6)))
with open(self.tmp_file, 'w') as fh:
fh.write("ordereddict==1.0.1")
self.assertEqual([
"ordereddict==1.0.1"],
packaging.parse_requirements([self.tmp_file]))
def test_parse_requirements_override_with_env(self): def test_parse_requirements_override_with_env(self):
with open(self.tmp_file, 'w') as fh: with open(self.tmp_file, 'w') as fh:
fh.write("foo\nbar") fh.write("foo\nbar")