summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMonty Taylor <mordred@inaugust.com>2013-07-12 03:03:18 -0400
committerMonty Taylor <mordred@inaugust.com>2013-07-12 03:03:18 -0400
commit5ebf34cc1701dbd9925631e453f39cc8125e600f (patch)
tree1da23b86379d362e77f27bfbaa6305ac4b35c34e
parentb24d4ca203d2af8b5600a36946cae02ee6af7ad7 (diff)
parenta61eae656b839f1135f83844564fb3e4608309c6 (diff)
downloadpbr-5ebf34cc1701dbd9925631e453f39cc8125e600f.tar.gz
Merge feature/merged2to1 into master
Upstream d2to1 has been rather unresponsive, and doing what we need in this case is really easier without the extra complexity. Change-Id: Ibd16944e76ad8398b57b6ddcbcd150cd462add3e
-rw-r--r--.mailmap2
-rw-r--r--README.rst23
-rw-r--r--pbr/core.py127
-rw-r--r--pbr/d2to1/__init__.py0
-rw-r--r--pbr/d2to1/tests/__init__.py86
-rw-r--r--pbr/d2to1/tests/test_commands.py58
-rw-r--r--pbr/d2to1/tests/test_core.py75
-rw-r--r--pbr/d2to1/tests/test_hooks.py93
-rw-r--r--pbr/d2to1/tests/testpackage/CHANGES.txt86
-rw-r--r--pbr/d2to1/tests/testpackage/LICENSE.txt29
-rw-r--r--pbr/d2to1/tests/testpackage/MANIFEST.in1
-rw-r--r--pbr/d2to1/tests/testpackage/README.txt148
-rw-r--r--pbr/d2to1/tests/testpackage/d2to1_testpackage/__init__.py0
-rw-r--r--pbr/d2to1/tests/testpackage/d2to1_testpackage/_setup_hooks.py65
-rw-r--r--pbr/d2to1/tests/testpackage/d2to1_testpackage/package_data/1.txt0
-rw-r--r--pbr/d2to1/tests/testpackage/d2to1_testpackage/package_data/2.txt0
-rw-r--r--pbr/d2to1/tests/testpackage/data_files/a.txt0
-rw-r--r--pbr/d2to1/tests/testpackage/data_files/b.txt0
-rw-r--r--pbr/d2to1/tests/testpackage/data_files/c.rst0
-rw-r--r--pbr/d2to1/tests/testpackage/extra-file.txt0
-rw-r--r--pbr/d2to1/tests/testpackage/setup.cfg46
-rwxr-xr-xpbr/d2to1/tests/testpackage/setup.py22
-rw-r--r--pbr/d2to1/tests/testpackage/src/testext.c28
-rw-r--r--pbr/d2to1/tests/util.py74
-rw-r--r--pbr/d2to1/util.py624
-rw-r--r--pbr/packaging.py36
-rw-r--r--pbr/testr_command.py15
-rw-r--r--pbr/tests/test_setup.py14
-rw-r--r--pbr/tests/test_version.py2
-rw-r--r--requirements.txt1
-rw-r--r--setup.cfg5
-rwxr-xr-xsetup.py5
-rw-r--r--tox.ini2
33 files changed, 1637 insertions, 30 deletions
diff --git a/.mailmap b/.mailmap
index 18221d4..373b96a 100644
--- a/.mailmap
+++ b/.mailmap
@@ -1,4 +1,6 @@
# Format is:
# <preferred e-mail> <other e-mail 1>
# <preferred e-mail> <other e-mail 2>
+Davanum Srinivas <dims@linux.vnet.ibm.com> <davanum@gmail.com>
+Erik M. Bray <embray@stsci.edu> Erik Bray <embray@stsci.edu>
Zhongyue Luo <zhongyue.nah@intel.com> <lzyeval@gmail.com>
diff --git a/README.rst b/README.rst
index 146fa40..09e4135 100644
--- a/README.rst
+++ b/README.rst
@@ -13,9 +13,20 @@ it's simple and repeatable. If you want to do things differently, cool! But
you've already got the power of python at your fingertips, so you don't
really need PBR.
-PBR builds on top of `d2to1` to provide for declarative configuration. It
-then filters the `setup.cfg` data through a setup hook to fill in default
-values and provide more sensible behaviors.
+PBR builds on top of the work that `d2to1` started to provide for declarative
+configuration. `d2to1` is itself an implementation of the ideas behind
+`distutils2`. Although `distutils2` is now abandoned in favor of work towards
+PEP 426 and Metadata 2.0, declarative config is still a great idea and
+specifically important in trying to distribute setup code as a library
+when that library itself will alter how the setup is processed. As Metadata
+2.0 and other modern Python packaging PEPs come out, `pbr` aims to support
+them as quickly as possible.
+
+`pbr` reads and then filters the `setup.cfg` data through a setup hook to
+fill in default values and provide more sensible behaviors, and then feeds
+the results in as the arguments to a call to `setup.py` - so the heavy
+lifting of handling python packaging needs is still being done by
+`setuptools`.
Behaviors
=========
@@ -124,11 +135,11 @@ The minimal setup.py should look something like this::
from setuptools import setup
setup(
- setup_requires=['d2to1', 'pbr'],
- d2to1=True,
+ setup_requires=['pbr'],
+ pbr=True,
)
-Note that it's important to specify `d2to1=True` or else the pbr functionality
+Note that it's important to specify `pbr=True` or else the pbr functionality
will not be enabled.
It should also work fine if additional arguments are passed to `setup()`,
diff --git a/pbr/core.py b/pbr/core.py
new file mode 100644
index 0000000..8cee9b0
--- /dev/null
+++ b/pbr/core.py
@@ -0,0 +1,127 @@
+# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Copyright (C) 2013 Association of Universities for Research in Astronomy
+# (AURA)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# 3. The name of AURA and its representatives may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+
+from distutils import core
+from distutils import errors
+import os
+import sys
+import warnings
+
+from setuptools import dist
+
+from pbr.d2to1 import util
+
+
+core.Distribution = dist._get_unpatched(core.Distribution)
+if sys.version_info[0] == 3:
+ string_type = str
+ integer_types = int
+else:
+ string_type = basestring
+ integer_types = (int, long)
+
+
+def pbr(dist, attr, value):
+ """Implements the actual pbr setup() keyword. When used, this should be
+ the only keyword in your setup() aside from `setup_requires`.
+
+ If given as a string, the value of pbr is assumed to be the relative path
+ to the setup.cfg file to use. Otherwise, if it evaluates to true, it
+ simply assumes that pbr should be used, and the default 'setup.cfg' is
+ used.
+
+ This works by reading the setup.cfg file, parsing out the supported
+ metadata and command options, and using them to rebuild the
+ `DistributionMetadata` object and set the newly added command options.
+
+ The reason for doing things this way is that a custom `Distribution` class
+ will not play nicely with setup_requires; however, this implementation may
+ not work well with distributions that do use a `Distribution` subclass.
+ """
+
+ if not value:
+ return
+ if isinstance(value, string_type):
+ path = os.path.abspath(value)
+ else:
+ path = os.path.abspath('setup.cfg')
+ if not os.path.exists(path):
+ raise errors.DistutilsFileError(
+ 'The setup.cfg file %s does not exist.' % path)
+
+ # Converts the setup.cfg file to setup() arguments
+ try:
+ attrs = util.cfg_to_args(path)
+ except Exception:
+ e = sys.exc_info()[1]
+ raise errors.DistutilsSetupError(
+ 'Error parsing %s: %s: %s' % (path, e.__class__.__name__, e))
+
+ # Repeat some of the Distribution initialization code with the newly
+ # provided attrs
+ if attrs:
+ # Skips 'options' and 'licence' support which are rarely used; may add
+ # back in later if demanded
+ for key, val in attrs.items():
+ if hasattr(dist.metadata, 'set_' + key):
+ getattr(dist.metadata, 'set_' + key)(val)
+ elif hasattr(dist.metadata, key):
+ setattr(dist.metadata, key, val)
+ elif hasattr(dist, key):
+ setattr(dist, key, val)
+ else:
+ msg = 'Unknown distribution option: %s' % repr(key)
+ warnings.warn(msg)
+
+ # Re-finalize the underlying Distribution
+ core.Distribution.finalize_options(dist)
+
+ # This bit comes out of distribute/setuptools
+ if isinstance(dist.metadata.version, integer_types + (float,)):
+ # Some people apparently take "version number" too literally :)
+ dist.metadata.version = str(dist.metadata.version)
+
+ # This bit of hackery is necessary so that the Distribution will ignore
+ # normally unsupport command options (namely pre-hooks and post-hooks).
+ # dist.command_options is normally a dict mapping command names to dicts of
+ # their options. Now it will be a defaultdict that returns IgnoreDicts for
+ # the each command's options so we can pass through the unsupported options
+ ignore = ['pre_hook.*', 'post_hook.*']
+ dist.command_options = util.DefaultGetDict(lambda: util.IgnoreDict(ignore))
diff --git a/pbr/d2to1/__init__.py b/pbr/d2to1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pbr/d2to1/__init__.py
diff --git a/pbr/d2to1/tests/__init__.py b/pbr/d2to1/tests/__init__.py
new file mode 100644
index 0000000..c9144bb
--- /dev/null
+++ b/pbr/d2to1/tests/__init__.py
@@ -0,0 +1,86 @@
+# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Copyright (C) 2013 Association of Universities for Research in Astronomy
+# (AURA)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# 3. The name of AURA and its representatives may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+
+import os
+import shutil
+import subprocess
+import sys
+
+import fixtures
+import testtools
+
+
+class D2to1TestCase(testtools.TestCase):
+ def setUp(self):
+ super(D2to1TestCase, self).setUp()
+ self.temp_dir = self.useFixture(fixtures.TempDir()).path
+ self.package_dir = os.path.join(self.temp_dir, 'testpackage')
+ shutil.copytree(os.path.join(os.path.dirname(__file__), 'testpackage'),
+ self.package_dir)
+ self.addCleanup(os.chdir, os.getcwd())
+ os.chdir(self.package_dir)
+
+ def tearDown(self):
+ # Remove d2to1.testpackage from sys.modules so that it can be freshly
+ # re-imported by the next test
+ for k in list(sys.modules):
+ if (k == 'd2to1_testpackage' or
+ k.startswith('d2to1_testpackage.')):
+ del sys.modules[k]
+ super(D2to1TestCase, self).tearDown()
+
+ def run_setup(self, *args):
+ return self._run_cmd(sys.executable, ('setup.py',) + args)
+
+ def _run_cmd(self, cmd, args):
+ """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
+ working copy--returns the stdout and stderr streams and the exit code
+ from the subprocess.
+ """
+
+ os.chdir(self.package_dir)
+ p = subprocess.Popen([cmd] + list(args), stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+
+ streams = tuple(s.decode('latin1').strip() for s in p.communicate())
+ print(streams)
+ return (streams) + (p.returncode,)
diff --git a/pbr/d2to1/tests/test_commands.py b/pbr/d2to1/tests/test_commands.py
new file mode 100644
index 0000000..6dbbd3c
--- /dev/null
+++ b/pbr/d2to1/tests/test_commands.py
@@ -0,0 +1,58 @@
+# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Copyright (C) 2013 Association of Universities for Research in Astronomy
+# (AURA)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# 3. The name of AURA and its representatives may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+
+from testtools import content
+
+from pbr.d2to1 import tests
+
+
+class TestCommands(tests.D2to1TestCase):
+ def test_custom_build_py_command(self):
+ """Test custom build_py command.
+
+ Test that a custom subclass of the build_py command runs when listed in
+ the commands [global] option, rather than the normal build command.
+ """
+
+ stdout, stderr, return_code = self.run_setup('build_py')
+ self.addDetail('stdout', content.text_content(stdout))
+ self.addDetail('stderr', content.text_content(stderr))
+ self.assertIn('Running custom build_py command.', stdout)
+ self.assertEqual(return_code, 0)
diff --git a/pbr/d2to1/tests/test_core.py b/pbr/d2to1/tests/test_core.py
new file mode 100644
index 0000000..092eca8
--- /dev/null
+++ b/pbr/d2to1/tests/test_core.py
@@ -0,0 +1,75 @@
+# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Copyright (C) 2013 Association of Universities for Research in Astronomy
+# (AURA)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# 3. The name of AURA and its representatives may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+
+import glob
+import os
+import tarfile
+
+from pbr.d2to1 import tests
+
+
+class TestCore(tests.D2to1TestCase):
+
+ def test_setup_py_keywords(self):
+ """setup.py --keywords.
+
+ Test that the `./setup.py --keywords` command returns the correct
+ value without balking.
+ """
+
+ self.run_setup('egg_info')
+ stdout, _, _ = self.run_setup('--keywords')
+ assert stdout == 'packaging,distutils,setuptools'
+
+ def test_sdist_extra_files(self):
+ """Test that the extra files are correctly added."""
+
+ stdout, _, return_code = self.run_setup('sdist', '--formats=gztar')
+
+ # There can be only one
+ try:
+ tf_path = glob.glob(os.path.join('dist', '*.tar.gz'))[0]
+ except IndexError:
+ assert False, 'source dist not found'
+
+ tf = tarfile.open(tf_path)
+ names = ['/'.join(p.split('/')[1:]) for p in tf.getnames()]
+
+ assert 'extra-file.txt' in names
diff --git a/pbr/d2to1/tests/test_hooks.py b/pbr/d2to1/tests/test_hooks.py
new file mode 100644
index 0000000..a9e9864
--- /dev/null
+++ b/pbr/d2to1/tests/test_hooks.py
@@ -0,0 +1,93 @@
+# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Copyright (C) 2013 Association of Universities for Research in Astronomy
+# (AURA)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# 3. The name of AURA and its representatives may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+
+import os
+import textwrap
+
+from pbr.d2to1 import tests
+from pbr.d2to1.tests import util
+
+
+class TestHooks(tests.D2to1TestCase):
+ def setUp(self):
+ super(TestHooks, self).setUp()
+ with util.open_config(
+ os.path.join(self.package_dir, 'setup.cfg')) as cfg:
+ cfg.set('global', 'setup-hooks',
+ 'd2to1_testpackage._setup_hooks.test_hook_1\n'
+ 'd2to1_testpackage._setup_hooks.test_hook_2')
+ cfg.set('build_ext', 'pre-hook.test_pre_hook',
+ 'd2to1_testpackage._setup_hooks.test_pre_hook')
+ cfg.set('build_ext', 'post-hook.test_post_hook',
+ 'd2to1_testpackage._setup_hooks.test_post_hook')
+
+ def test_global_setup_hooks(self):
+ """Test setup_hooks.
+
+ Test that setup_hooks listed in the [global] section of setup.cfg are
+ executed in order.
+ """
+
+ stdout, _, return_code = self.run_setup('egg_info')
+ assert 'test_hook_1\ntest_hook_2' in stdout
+ assert return_code == 0
+
+ def test_command_hooks(self):
+ """Test command hooks.
+
+ Simple test that the appropriate command hooks run at the
+ beginning/end of the appropriate command.
+ """
+
+ stdout, _, return_code = self.run_setup('egg_info')
+ assert 'build_ext pre-hook' not in stdout
+ assert 'build_ext post-hook' not in stdout
+ assert return_code == 0
+
+ stdout, _, return_code = self.run_setup('build_ext')
+ assert textwrap.dedent("""
+ running build_ext
+ running pre_hook d2to1_testpackage._setup_hooks.test_pre_hook for command build_ext
+ build_ext pre-hook
+ """) in stdout # flake8: noqa
+ assert stdout.endswith('build_ext post-hook')
+ assert return_code == 0
+
+
diff --git a/pbr/d2to1/tests/testpackage/CHANGES.txt b/pbr/d2to1/tests/testpackage/CHANGES.txt
new file mode 100644
index 0000000..709b9d4
--- /dev/null
+++ b/pbr/d2to1/tests/testpackage/CHANGES.txt
@@ -0,0 +1,86 @@
+Changelog
+===========
+
+0.3 (unreleased)
+------------------
+
+- The ``glob_data_files`` hook became a pre-command hook for the install_data
+ command instead of being a setup-hook. This is to support the additional
+ functionality of requiring data_files with relative destination paths to be
+ install relative to the package's install path (i.e. site-packages).
+
+- Dropped support for and deprecated the easier_install custom command.
+ Although it should still work, it probably won't be used anymore for
+ stsci_python packages.
+
+- Added support for the ``build_optional_ext`` command, which replaces/extends
+ the default ``build_ext`` command. See the README for more details.
+
+- Added the ``tag_svn_revision`` setup_hook as a replacement for the
+ setuptools-specific tag_svn_revision option to the egg_info command. This
+ new hook is easier to use than the old tag_svn_revision option: It's
+ automatically enabled by the presence of ``.dev`` in the version string, and
+ disabled otherwise.
+
+- The ``svn_info_pre_hook`` and ``svn_info_post_hook`` have been replaced with
+ ``version_pre_command_hook`` and ``version_post_command_hook`` respectively.
+ However, a new ``version_setup_hook``, which has the same purpose, has been
+ added. It is generally easier to use and will give more consistent results
+ in that it will run every time setup.py is run, regardless of which command
+ is used. ``stsci.distutils`` itself uses this hook--see the `setup.cfg` file
+ and `stsci/distutils/__init__.py` for example usage.
+
+- Instead of creating an `svninfo.py` module, the new ``version_`` hooks create
+ a file called `version.py`. In addition to the SVN info that was included
+ in `svninfo.py`, it includes a ``__version__`` variable to be used by the
+ package's `__init__.py`. This allows there to be a hard-coded
+ ``__version__`` variable included in the source code, rather than using
+ pkg_resources to get the version.
+
+- In `version.py`, the variables previously named ``__svn_version__`` and
+ ``__full_svn_info__`` are now named ``__svn_revision__`` and
+ ``__svn_full_info__``.
+
+- Fixed a bug when using stsci.distutils in the installation of other packages
+ in the ``stsci.*`` namespace package. If stsci.distutils was not already
+ installed, and was downloaded automatically by distribute through the
+ setup_requires option, then ``stsci.distutils`` would fail to import. This
+ is because the way the namespace package (nspkg) mechanism currently works,
+ all packages belonging to the nspkg *must* be on the import path at initial
+ import time.
+
+ So when installing stsci.tools, for example, if ``stsci.tools`` is imported
+ from within the source code at install time, but before ``stsci.distutils``
+ is downloaded and added to the path, the ``stsci`` package is already
+ imported and can't be extended to include the path of ``stsci.distutils``
+ after the fact. The easiest way of dealing with this, it seems, is to
+ delete ``stsci`` from ``sys.modules``, which forces it to be reimported, now
+ the its ``__path__`` extended to include ``stsci.distutil``'s path.
+
+
+0.2.2 (2011-11-09)
+------------------
+
+- Fixed check for the issue205 bug on actual setuptools installs; before it
+ only worked on distribute. setuptools has the issue205 bug prior to version
+ 0.6c10.
+
+- Improved the fix for the issue205 bug, especially on setuptools.
+ setuptools, prior to 0.6c10, did not back of sys.modules either before
+ sandboxing, which causes serious problems. In fact, it's so bad that it's
+ not enough to add a sys.modules backup to the current sandbox: It's in fact
+ necessary to monkeypatch setuptools.sandbox.run_setup so that any subsequent
+ calls to it also back up sys.modules.
+
+
+0.2.1 (2011-09-02)
+------------------
+
+- Fixed the dependencies so that setuptools is requirement but 'distribute'
+ specifically. Previously installation could fail if users had plain
+ setuptools installed and not distribute
+
+0.2 (2011-08-23)
+------------------
+
+- Initial public release
diff --git a/pbr/d2to1/tests/testpackage/LICENSE.txt b/pbr/d2to1/tests/testpackage/LICENSE.txt
new file mode 100644
index 0000000..7e8019a
--- /dev/null
+++ b/pbr/d2to1/tests/testpackage/LICENSE.txt
@@ -0,0 +1,29 @@
+Copyright (C) 2005 Association of Universities for Research in Astronomy (AURA)
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ 3. The name of AURA and its representatives may not be used to
+ endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
+
diff --git a/pbr/d2to1/tests/testpackage/MANIFEST.in b/pbr/d2to1/tests/testpackage/MANIFEST.in
new file mode 100644
index 0000000..cdc95ea
--- /dev/null
+++ b/pbr/d2to1/tests/testpackage/MANIFEST.in
@@ -0,0 +1 @@
+include data_files/*
diff --git a/pbr/d2to1/tests/testpackage/README.txt b/pbr/d2to1/tests/testpackage/README.txt
new file mode 100644
index 0000000..4f00d32
--- /dev/null
+++ b/pbr/d2to1/tests/testpackage/README.txt
@@ -0,0 +1,148 @@
+Introduction
+============
+This package contains utilities used to package some of STScI's Python
+projects; specifically those projects that comprise stsci_python_ and
+Astrolib_.
+
+It currently consists mostly of some setup_hook scripts meant for use with
+`distutils2/packaging`_ and/or d2to1_, and a customized easy_install command
+meant for use with distribute_.
+
+This package is not meant for general consumption, though it might be worth
+looking at for examples of how to do certain things with your own packages, but
+YMMV.
+
+Features
+========
+
+Hook Scripts
+------------
+Currently the main features of this package are a couple of setup_hook scripts.
+In distutils2, a setup_hook is a script that runs at the beginning of any
+pysetup command, and can modify the package configuration read from setup.cfg.
+There are also pre- and post-command hooks that only run before/after a
+specific setup command (eg. build_ext, install) is run.
+
+stsci.distutils.hooks.use_packages_root
+'''''''''''''''''''''''''''''''''''''''
+If using the ``packages_root`` option under the ``[files]`` section of
+setup.cfg, this hook will add that path to ``sys.path`` so that modules in your
+package can be imported and used in setup. This can be used even if
+``packages_root`` is not specified--in this case it adds ``''`` to
+``sys.path``.
+
+stsci.distutils.hooks.version_setup_hook
+''''''''''''''''''''''''''''''''''''''''
+Creates a Python module called version.py which currently contains four
+variables:
+
+* ``__version__`` (the release version)
+* ``__svn_revision__`` (the SVN revision info as returned by the ``svnversion``
+ command)
+* ``__svn_full_info__`` (as returned by the ``svn info`` command)
+* ``__setup_datetime__`` (the date and time that setup.py was last run).
+
+These variables can be imported in the package's `__init__.py` for degugging
+purposes. The version.py module will *only* be created in a package that
+imports from the version module in its `__init__.py`. It should be noted that
+this is generally preferable to writing these variables directly into
+`__init__.py`, since this provides more control and is less likely to
+unexpectedly break things in `__init__.py`.
+
+stsci.distutils.hooks.version_pre_command_hook
+''''''''''''''''''''''''''''''''''''''''''''''
+Identical to version_setup_hook, but designed to be used as a pre-command
+hook.
+
+stsci.distutils.hooks.version_post_command_hook
+'''''''''''''''''''''''''''''''''''''''''''''''
+The complement to version_pre_command_hook. This will delete any version.py
+files created during a build in order to prevent them from cluttering an SVN
+working copy (note, however, that version.py is *not* deleted from the build/
+directory, so a copy of it is still preserved). It will also not be deleted
+if the current directory is not an SVN working copy. For example, if source
+code extracted from a source tarball it will be preserved.
+
+stsci.distutils.hooks.tag_svn_revision
+''''''''''''''''''''''''''''''''''''''
+A setup_hook to add the SVN revision of the current working copy path to the
+package version string, but only if the version ends in .dev.
+
+For example, ``mypackage-1.0.dev`` becomes ``mypackage-1.0.dev1234``. This is
+in accordance with the version string format standardized by PEP 386.
+
+This should be used as a replacement for the ``tag_svn_revision`` option to
+the egg_info command. This hook is more compatible with packaging/distutils2,
+which does not include any VCS support. This hook is also more flexible in
+that it turns the revision number on/off depending on the presence of ``.dev``
+in the version string, so that it's not automatically added to the version in
+final releases.
+
+This hook does require the ``svnversion`` command to be available in order to
+work. It does not examine the working copy metadata directly.
+
+stsci.distutils.hooks.numpy_extension_hook
+''''''''''''''''''''''''''''''''''''''''''
+This is a pre-command hook for the build_ext command. To use it, add a
+``[build_ext]`` section to your setup.cfg, and add to it::
+
+ pre-hook.numpy-extension-hook = stsci.distutils.hooks.numpy_extension_hook
+
+This hook must be used to build extension modules that use Numpy. The primary
+side-effect of this hook is to add the correct numpy include directories to
+`include_dirs`. To use it, add 'numpy' to the 'include-dirs' option of each
+extension module that requires numpy to build. The value 'numpy' will be
+replaced with the actual path to the numpy includes.
+
+stsci.distutils.hooks.is_display_option
+'''''''''''''''''''''''''''''''''''''''
+This is not actually a hook, but is a useful utility function that can be used
+in writing other hooks. Basically, it returns ``True`` if setup.py was run
+with a "display option" such as --version or --help. This can be used to
+prevent your hook from running in such cases.
+
+stsci.distutils.hooks.glob_data_files
+'''''''''''''''''''''''''''''''''''''
+A pre-command hook for the install_data command. Allows filename wildcards as
+understood by ``glob.glob()`` to be used in the data_files option. This hook
+must be used in order to have this functionality since it does not normally
+exist in distutils.
+
+This hook also ensures that data files are installed relative to the package
+path. data_files shouldn't normally be installed this way, but the
+functionality is required for a few special cases.
+
+
+Commands
+--------
+build_optional_ext
+''''''''''''''''''
+This serves as an optional replacement for the default built_ext command,
+which compiles C extension modules. Its purpose is to allow extension modules
+to be *optional*, so that if their build fails the rest of the package is
+still allowed to be built and installed. This can be used when an extension
+module is not definitely required to use the package.
+
+To use this custom command, add::
+
+ commands = stsci.distutils.command.build_optional_ext.build_optional_ext
+
+under the ``[global]`` section of your package's setup.cfg. Then, to mark
+an individual extension module as optional, under the setup.cfg section for
+that extension add::
+
+ optional = True
+
+Optionally, you may also add a custom failure message by adding::
+
+ fail_message = The foobar extension module failed to compile.
+ This could be because you lack such and such headers.
+ This package will still work, but such and such features
+ will be disabled.
+
+
+.. _stsci_python: http://www.stsci.edu/resources/software_hardware/pyraf/stsci_python
+.. _Astrolib: http://www.scipy.org/AstroLib/
+.. _distutils2/packaging: http://distutils2.notmyidea.org/
+.. _d2to1: http://pypi.python.org/pypi/d2to1
+.. _distribute: http://pypi.python.org/pypi/distribute
diff --git a/pbr/d2to1/tests/testpackage/d2to1_testpackage/__init__.py b/pbr/d2to1/tests/testpackage/d2to1_testpackage/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pbr/d2to1/tests/testpackage/d2to1_testpackage/__init__.py
diff --git a/pbr/d2to1/tests/testpackage/d2to1_testpackage/_setup_hooks.py b/pbr/d2to1/tests/testpackage/d2to1_testpackage/_setup_hooks.py
new file mode 100644
index 0000000..f8b3087
--- /dev/null
+++ b/pbr/d2to1/tests/testpackage/d2to1_testpackage/_setup_hooks.py
@@ -0,0 +1,65 @@
+# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Copyright (C) 2013 Association of Universities for Research in Astronomy
+# (AURA)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# 3. The name of AURA and its representatives may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+
+from distutils.command import build_py
+
+
+def test_hook_1(config):
+ print('test_hook_1')
+
+
+def test_hook_2(config):
+ print('test_hook_2')
+
+
+class test_command(build_py.build_py):
+ command_name = 'build_py'
+
+ def run(self):
+ print('Running custom build_py command.')
+ return build_py.build_py.run(self)
+
+
+def test_pre_hook(cmdobj):
+ print('build_ext pre-hook')
+
+
+def test_post_hook(cmdobj):
+ print('build_ext post-hook')
diff --git a/pbr/d2to1/tests/testpackage/d2to1_testpackage/package_data/1.txt b/pbr/d2to1/tests/testpackage/d2to1_testpackage/package_data/1.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pbr/d2to1/tests/testpackage/d2to1_testpackage/package_data/1.txt
diff --git a/pbr/d2to1/tests/testpackage/d2to1_testpackage/package_data/2.txt b/pbr/d2to1/tests/testpackage/d2to1_testpackage/package_data/2.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pbr/d2to1/tests/testpackage/d2to1_testpackage/package_data/2.txt
diff --git a/pbr/d2to1/tests/testpackage/data_files/a.txt b/pbr/d2to1/tests/testpackage/data_files/a.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pbr/d2to1/tests/testpackage/data_files/a.txt
diff --git a/pbr/d2to1/tests/testpackage/data_files/b.txt b/pbr/d2to1/tests/testpackage/data_files/b.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pbr/d2to1/tests/testpackage/data_files/b.txt
diff --git a/pbr/d2to1/tests/testpackage/data_files/c.rst b/pbr/d2to1/tests/testpackage/data_files/c.rst
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pbr/d2to1/tests/testpackage/data_files/c.rst
diff --git a/pbr/d2to1/tests/testpackage/extra-file.txt b/pbr/d2to1/tests/testpackage/extra-file.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pbr/d2to1/tests/testpackage/extra-file.txt
diff --git a/pbr/d2to1/tests/testpackage/setup.cfg b/pbr/d2to1/tests/testpackage/setup.cfg
new file mode 100644
index 0000000..a200616
--- /dev/null
+++ b/pbr/d2to1/tests/testpackage/setup.cfg
@@ -0,0 +1,46 @@
+[metadata]
+name = d2to1_testpackage
+version = 0.1.dev
+author = Erik M. Bray
+author-email = embray@stsci.edu
+home-page = http://www.stsci.edu/resources/software_hardware/stsci_python
+summary = Test package for testing d2to1
+description-file =
+ README.txt
+ CHANGES.txt
+requires-python = >=2.5
+
+requires-dist =
+ setuptools
+
+classifier =
+ Development Status :: 3 - Alpha
+ Intended Audience :: Developers
+ License :: OSI Approved :: BSD License
+ Programming Language :: Python
+ Topic :: Scientific/Engineering
+ Topic :: Software Development :: Build Tools
+ Topic :: Software Development :: Libraries :: Python Modules
+ Topic :: System :: Archiving :: Packaging
+
+keywords = packaging, distutils, setuptools
+
+[files]
+packages = d2to1_testpackage
+package-data = testpackage = package_data/*.txt
+data-files = testpackage/data_files = data_files/*.txt
+extra-files = extra-file.txt
+
+[extension=d2to1_testpackage.testext]
+sources = src/testext.c
+optional = True
+
+[global]
+#setup-hooks =
+# d2to1_testpackage._setup_hooks.test_hook_1
+# d2to1_testpackage._setup_hooks.test_hook_2
+commands = d2to1_testpackage._setup_hooks.test_command
+
+[build_ext]
+#pre-hook.test_pre_hook = d2to1_testpackage._setup_hooks.test_pre_hook
+#post-hook.test_post_hook = d2to1_testpackage._setup_hooks.test_post_hook
diff --git a/pbr/d2to1/tests/testpackage/setup.py b/pbr/d2to1/tests/testpackage/setup.py
new file mode 100755
index 0000000..8866691
--- /dev/null
+++ b/pbr/d2to1/tests/testpackage/setup.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import setuptools
+
+setuptools.setup(
+ setup_requires=['pbr'],
+ pbr=True,
+)
diff --git a/pbr/d2to1/tests/testpackage/src/testext.c b/pbr/d2to1/tests/testpackage/src/testext.c
new file mode 100644
index 0000000..872d43c
--- /dev/null
+++ b/pbr/d2to1/tests/testpackage/src/testext.c
@@ -0,0 +1,28 @@
+#include <Python.h>
+
+
+static PyMethodDef TestextMethods[] = {
+ {NULL, NULL, 0, NULL}
+};
+
+
+#if PY_MAJOR_VERSION >=3
+static struct PyModuleDef testextmodule = {
+ PyModuleDef_HEAD_INIT,
+ "testext",
+ -1,
+ TestextMethods
+};
+
+PyObject*
+PyInit_testext(void)
+{
+ return PyModule_Create(&testextmodule);
+}
+#else
+PyMODINIT_FUNC
+inittestext(void)
+{
+ Py_InitModule("testext", TestextMethods);
+}
+#endif
diff --git a/pbr/d2to1/tests/util.py b/pbr/d2to1/tests/util.py
new file mode 100644
index 0000000..e657508
--- /dev/null
+++ b/pbr/d2to1/tests/util.py
@@ -0,0 +1,74 @@
+# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Copyright (C) 2013 Association of Universities for Research in Astronomy
+# (AURA)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# 3. The name of AURA and its representatives may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+
+import contextlib
+import os
+import shutil
+import stat
+
+try:
+ import configparser
+except ImportError:
+ import ConfigParser as configparser
+
+
+@contextlib.contextmanager
+def open_config(filename):
+ cfg = configparser.ConfigParser()
+ cfg.read(filename)
+ yield cfg
+ with open(filename, 'w') as fp:
+ cfg.write(fp)
+
+
+def rmtree(path):
+ """shutil.rmtree() with error handler.
+
+ Handle 'access denied' from trying to delete read-only files.
+ """
+
+ def onerror(func, path, exc_info):
+ if not os.access(path, os.W_OK):
+ os.chmod(path, stat.S_IWUSR)
+ func(path)
+ else:
+ raise
+
+ return shutil.rmtree(path, onerror=onerror)
diff --git a/pbr/d2to1/util.py b/pbr/d2to1/util.py
new file mode 100644
index 0000000..583c6b8
--- /dev/null
+++ b/pbr/d2to1/util.py
@@ -0,0 +1,624 @@
+# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Copyright (C) 2013 Association of Universities for Research in Astronomy
+# (AURA)
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# 3. The name of AURA and its representatives may not be used to
+# endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+
+"""The code in this module is mostly copy/pasted out of the distutils2 source
+code, as recommended by Tarek Ziade. As such, it may be subject to some change
+as distutils2 development continues, and will have to be kept up to date.
+
+I didn't want to use it directly from distutils2 itself, since I do not want it
+to be an installation dependency for our packages yet--it is still too unstable
+(the latest version on PyPI doesn't even install).
+"""
+
+# These first two imports are not used, but are needed to get around an
+# irritating Python bug that can crop up when using ./setup.py test.
+# See: http://www.eby-sarna.com/pipermail/peak/2010-May/003355.html
+try:
+ import multiprocessing # flake8: noqa
+except ImportError:
+ pass
+import logging # flake8: noqa
+
+import os
+import re
+import sys
+import traceback
+
+from collections import defaultdict
+
+import distutils.ccompiler
+
+from distutils import log
+from distutils.errors import (DistutilsOptionError, DistutilsModuleError,
+ DistutilsFileError)
+from setuptools.command.egg_info import manifest_maker
+from setuptools.dist import Distribution
+from setuptools.extension import Extension
+
+try:
+ import configparser
+except ImportError:
+ import ConfigParser as configparser
+
+
+# A simplified RE for this; just checks that the line ends with version
+# predicates in ()
+_VERSION_SPEC_RE = re.compile(r'\s*(.*?)\s*\((.*)\)\s*$')
+
+
+# Mappings from setup() keyword arguments to setup.cfg options;
+# The values are (section, option) tuples, or simply (section,) tuples if
+# the option has the same name as the setup() argument
+D1_D2_SETUP_ARGS = {
+ "name": ("metadata",),
+ "version": ("metadata",),
+ "author": ("metadata",),
+ "author_email": ("metadata",),
+ "maintainer": ("metadata",),
+ "maintainer_email": ("metadata",),
+ "url": ("metadata", "home_page"),
+ "description": ("metadata", "summary"),
+ "keywords": ("metadata",),
+ "long_description": ("metadata", "description"),
+ "download-url": ("metadata",),
+ "classifiers": ("metadata", "classifier"),
+ "platforms": ("metadata", "platform"), # **
+ "license": ("metadata",),
+ # Use setuptools install_requires, not
+ # broken distutils requires
+ "install_requires": ("metadata", "requires_dist"),
+ "setup_requires": ("metadata", "setup_requires_dist"),
+ "provides": ("metadata", "provides_dist"), # **
+ "obsoletes": ("metadata", "obsoletes_dist"), # **
+ "package_dir": ("files", 'packages_root'),
+ "packages": ("files",),
+ "package_data": ("files",),
+ "namespace_packages": ("files",),
+ "data_files": ("files",),
+ "scripts": ("files",),
+ "py_modules": ("files", "modules"), # **
+ "cmdclass": ("global", "commands"),
+ # Not supported in distutils2, but provided for
+ # backwards compatibility with setuptools
+ "use_2to3": ("backwards_compat", "use_2to3"),
+ "zip_safe": ("backwards_compat", "zip_safe"),
+ "tests_require": ("backwards_compat", "tests_require"),
+ "dependency_links": ("backwards_compat",),
+ "include_package_data": ("backwards_compat",),
+}
+
+# setup() arguments that can have multiple values in setup.cfg
+MULTI_FIELDS = ("classifiers",
+ "platforms",
+ "install_requires",
+ "provides",
+ "obsoletes",
+ "namespace_packages",
+ "packages",
+ "package_data",
+ "data_files",
+ "scripts",
+ "py_modules",
+ "dependency_links",
+ "setup_requires",
+ "tests_require",
+ "cmdclass")
+
+# setup() arguments that contain boolean values
+BOOL_FIELDS = ("use_2to3", "zip_safe", "include_package_data")
+
+
+CSV_FIELDS = ("keywords",)
+
+
+log.set_verbosity(log.INFO)
+
+
+def resolve_name(name):
+ """Resolve a name like ``module.object`` to an object and return it.
+
+ Raise ImportError if the module or name is not found.
+ """
+
+ parts = name.split('.')
+ cursor = len(parts) - 1
+ module_name = parts[:cursor]
+ attr_name = parts[-1]
+
+ while cursor > 0:
+ try:
+ ret = __import__('.'.join(module_name), fromlist=[attr_name])
+ break
+ except ImportError:
+ if cursor == 0:
+ raise
+ cursor -= 1
+ module_name = parts[:cursor]
+ attr_name = parts[cursor]
+ ret = ''
+
+ for part in parts[cursor:]:
+ try:
+ ret = getattr(ret, part)
+ except AttributeError:
+ raise ImportError(name)
+
+ return ret
+
+
+def cfg_to_args(path='setup.cfg'):
+ """ Distutils2 to distutils1 compatibility util.
+
+ This method uses an existing setup.cfg to generate a dictionary of
+ keywords that can be used by distutils.core.setup(kwargs**).
+
+ :param file:
+ The setup.cfg path.
+ :raises DistutilsFileError:
+ When the setup.cfg file is not found.
+
+ """
+
+ # The method source code really starts here.
+ parser = configparser.RawConfigParser()
+ if not os.path.exists(path):
+ raise DistutilsFileError("file '%s' does not exist" %
+ os.path.abspath(path))
+ parser.read(path)
+ config = {}
+ for section in parser.sections():
+ config[section] = dict(parser.items(section))
+
+ # Run setup_hooks, if configured
+ setup_hooks = has_get_option(config, 'global', 'setup_hooks')
+ package_dir = has_get_option(config, 'files', 'packages_root')
+
+ # Add the source package directory to sys.path in case it contains
+ # additional hooks, and to make sure it's on the path before any existing
+ # installations of the package
+ if package_dir:
+ package_dir = os.path.abspath(package_dir)
+ sys.path.insert(0, package_dir)
+
+ try:
+ if setup_hooks:
+ setup_hooks = split_multiline(setup_hooks)
+ for hook in setup_hooks:
+ hook_fn = resolve_name(hook)
+ try :
+ hook_fn(config)
+ except SystemExit:
+ log.error('setup hook %s terminated the installation')
+ except:
+ e = sys.exc_info()[1]
+ log.error('setup hook %s raised exception: %s\n' %
+ (hook, e))
+ log.error(traceback.format_exc())
+ sys.exit(1)
+
+ kwargs = setup_cfg_to_setup_kwargs(config)
+
+ register_custom_compilers(config)
+
+ ext_modules = get_extension_modules(config)
+ if ext_modules:
+ kwargs['ext_modules'] = ext_modules
+
+ entry_points = get_entry_points(config)
+ if entry_points:
+ kwargs['entry_points'] = entry_points
+
+ wrap_commands(kwargs)
+
+ # Handle the [files]/extra_files option
+ extra_files = has_get_option(config, 'files', 'extra_files')
+ if extra_files:
+ extra_files = split_multiline(extra_files)
+ # Let's do a sanity check
+ for filename in extra_files:
+ if not os.path.exists(filename):
+ raise DistutilsFileError(
+ '%s from the extra_files option in setup.cfg does not '
+ 'exist' % filename)
+ # Unfortunately the only really sensible way to do this is to
+ # monkey-patch the manifest_maker class
+ @monkeypatch_method(manifest_maker)
+ def add_defaults(self, extra_files=extra_files, log=log):
+ log.info('[d2to1] running patched manifest_maker command '
+ 'with extra_files support')
+ add_defaults._orig(self)
+ self.filelist.extend(extra_files)
+
+ finally:
+ # Perform cleanup if any paths were added to sys.path
+ if package_dir:
+ sys.path.pop(0)
+
+ return kwargs
+
+
+def setup_cfg_to_setup_kwargs(config):
+ """Processes the setup.cfg options and converts them to arguments accepted
+ by setuptools' setup() function.
+ """
+
+ kwargs = {}
+
+ for arg in D1_D2_SETUP_ARGS:
+ if len(D1_D2_SETUP_ARGS[arg]) == 2:
+ # The distutils field name is different than distutils2's.
+ section, option = D1_D2_SETUP_ARGS[arg]
+
+ elif len(D1_D2_SETUP_ARGS[arg]) == 1:
+ # The distutils field name is the same thant distutils2's.
+ section = D1_D2_SETUP_ARGS[arg][0]
+ option = arg
+
+ in_cfg_value = has_get_option(config, section, option)
+ if not in_cfg_value:
+ # There is no such option in the setup.cfg
+ if arg == "long_description":
+ in_cfg_value = has_get_option(config, section,
+ "description_file")
+ if in_cfg_value:
+ in_cfg_value = split_multiline(in_cfg_value)
+ value = ''
+ for filename in in_cfg_value:
+ description_file = open(filename)
+ try:
+ value += description_file.read().strip() + '\n\n'
+ finally:
+ description_file.close()
+ in_cfg_value = value
+ else:
+ continue
+
+ if arg in CSV_FIELDS:
+ in_cfg_value = split_csv(in_cfg_value)
+ if arg in MULTI_FIELDS:
+ in_cfg_value = split_multiline(in_cfg_value)
+ elif arg in BOOL_FIELDS:
+ # Provide some flexibility here...
+ if in_cfg_value.lower() in ('true', 't', '1', 'yes', 'y'):
+ in_cfg_value = True
+ else:
+ in_cfg_value = False
+
+ if in_cfg_value:
+ if arg in ('install_requires', 'tests_require'):
+ # Replaces PEP345-style version specs with the sort expected by
+ # setuptools
+ in_cfg_value = [_VERSION_SPEC_RE.sub(r'\1\2', pred)
+ for pred in in_cfg_value]
+ elif arg == 'package_dir':
+ in_cfg_value = {'': in_cfg_value}
+ elif arg in ('package_data', 'data_files'):
+ data_files = {}
+ firstline = True
+ prev = None
+ for line in in_cfg_value:
+ if '=' in line:
+ key, value = line.split('=', 1)
+ key, value = (key.strip(), value.strip())
+ if key in data_files:
+ # Multiple duplicates of the same package name;
+ # this is for backwards compatibility of the old
+ # format prior to d2to1 0.2.6.
+ prev = data_files[key]
+ prev.extend(value.split())
+ else:
+ prev = data_files[key.strip()] = value.split()
+ elif firstline:
+ raise DistutilsOptionError(
+ 'malformed package_data first line %r (misses '
+ '"=")' % line)
+ else:
+ prev.extend(line.strip().split())
+ firstline = False
+ if arg == 'data_files':
+ # the data_files value is a pointlessly different structure
+ # from the package_data value
+ data_files = data_files.items()
+ in_cfg_value = data_files
+ elif arg == 'cmdclass':
+ cmdclass = {}
+ dist = Distribution()
+ for cls in in_cfg_value:
+ cls = resolve_name(cls)
+ cmd = cls(dist)
+ cmdclass[cmd.get_command_name()] = cls
+ in_cfg_value = cmdclass
+
+ kwargs[arg] = in_cfg_value
+
+ return kwargs
+
+
+def register_custom_compilers(config):
+ """Handle custom compilers; this has no real equivalent in distutils, where
+ additional compilers could only be added programmatically, so we have to
+ hack it in somehow.
+ """
+
+ compilers = has_get_option(config, 'global', 'compilers')
+ if compilers:
+ compilers = split_multiline(compilers)
+ for compiler in compilers:
+ compiler = resolve_name(compiler)
+
+ # In distutils2 compilers these class attributes exist; for
+ # distutils1 we just have to make something up
+ if hasattr(compiler, 'name'):
+ name = compiler.name
+ else:
+ name = compiler.__name__
+ if hasattr(compiler, 'description'):
+ desc = compiler.description
+ else:
+ desc = 'custom compiler %s' % name
+
+ module_name = compiler.__module__
+ # Note; this *will* override built in compilers with the same name
+ # TODO: Maybe display a warning about this?
+ cc = distutils.ccompiler.compiler_class
+ cc[name] = (module_name, compiler.__name__, desc)
+
+ # HACK!!!! Distutils assumes all compiler modules are in the
+ # distutils package
+ sys.modules['distutils.' + module_name] = sys.modules[module_name]
+
+
+def get_extension_modules(config):
+ """Handle extension modules"""
+
+ EXTENSION_FIELDS = ("sources",
+ "include_dirs",
+ "define_macros",
+ "undef_macros",
+ "library_dirs",
+ "libraries",
+ "runtime_library_dirs",
+ "extra_objects",
+ "extra_compile_args",
+ "extra_link_args",
+ "export_symbols",
+ "swig_opts",
+ "depends")
+
+ ext_modules = []
+ for section in config:
+ if ':' in section:
+ labels = section.split(':', 1)
+ else:
+ # Backwards compatibility for old syntax; don't use this though
+ labels = section.split('=', 1)
+ labels = [l.strip() for l in labels]
+ if (len(labels) == 2) and (labels[0] == 'extension'):
+ ext_args = {}
+ for field in EXTENSION_FIELDS:
+ value = has_get_option(config, section, field)
+ # All extension module options besides name can have multiple
+ # values
+ if not value:
+ continue
+ value = split_multiline(value)
+ if field == 'define_macros':
+ macros = []
+ for macro in value:
+ macro = macro.split('=', 1)
+ if len(macro) == 1:
+ macro = (macro[0].strip(), None)
+ else:
+ macro = (macro[0].strip(), macro[1].strip())
+ macros.append(macro)
+ value = macros
+ ext_args[field] = value
+ if ext_args:
+ if 'name' not in ext_args:
+ ext_args['name'] = labels[1]
+ ext_modules.append(Extension(ext_args.pop('name'),
+ **ext_args))
+ return ext_modules
+
+
+def get_entry_points(config):
+ """Process the [entry_points] section of setup.cfg to handle setuptools
+ entry points. This is, of course, not a standard feature of
+ distutils2/packaging, but as there is not currently a standard alternative
+ in packaging, we provide support for them.
+ """
+
+ if not 'entry_points' in config:
+ return {}
+
+ return dict((option, split_multiline(value))
+ for option, value in config['entry_points'].items())
+
+
+def wrap_commands(kwargs):
+ dist = Distribution()
+
+ # This should suffice to get the same config values and command classes
+ # that the actual Distribution will see (not counting cmdclass, which is
+ # handled below)
+ dist.parse_config_files()
+
+ for cmd, _ in dist.get_command_list():
+ hooks = {}
+ for opt, val in dist.get_option_dict(cmd).items():
+ val = val[1]
+ if opt.startswith('pre_hook.') or opt.startswith('post_hook.'):
+ hook_type, alias = opt.split('.', 1)
+ hook_dict = hooks.setdefault(hook_type, {})
+ hook_dict[alias] = val
+ if not hooks:
+ continue
+
+ if 'cmdclass' in kwargs and cmd in kwargs['cmdclass']:
+ cmdclass = kwargs['cmdclass'][cmd]
+ else:
+ cmdclass = dist.get_command_class(cmd)
+
+ new_cmdclass = wrap_command(cmd, cmdclass, hooks)
+ kwargs.setdefault('cmdclass', {})[cmd] = new_cmdclass
+
+
+def wrap_command(cmd, cmdclass, hooks):
+ def run(self, cmdclass=cmdclass):
+ self.run_command_hooks('pre_hook')
+ cmdclass.run(self)
+ self.run_command_hooks('post_hook')
+
+ return type(cmd, (cmdclass, object),
+ {'run': run, 'run_command_hooks': run_command_hooks,
+ 'pre_hook': hooks.get('pre_hook'),
+ 'post_hook': hooks.get('post_hook')})
+
+
+def run_command_hooks(cmd_obj, hook_kind):
+ """Run hooks registered for that command and phase.
+
+ *cmd_obj* is a finalized command object; *hook_kind* is either
+ 'pre_hook' or 'post_hook'.
+ """
+
+ if hook_kind not in ('pre_hook', 'post_hook'):
+ raise ValueError('invalid hook kind: %r' % hook_kind)
+
+ hooks = getattr(cmd_obj, hook_kind, None)
+
+ if hooks is None:
+ return
+
+ for hook in hooks.values():
+ if isinstance(hook, str):
+ try:
+ hook_obj = resolve_name(hook)
+ except ImportError:
+ err = sys.exc_info()[1] # For py3k
+ raise DistutilsModuleError('cannot find hook %s: %s' %
+ (hook,err))
+ else:
+ hook_obj = hook
+
+ if not hasattr(hook_obj, '__call__'):
+ raise DistutilsOptionError('hook %r is not callable' % hook)
+
+ log.info('running %s %s for command %s',
+ hook_kind, hook, cmd_obj.get_command_name())
+
+ try :
+ hook_obj(cmd_obj)
+ except:
+ e = sys.exc_info()[1]
+ log.error('hook %s raised exception: %s\n' % (hook, e))
+ log.error(traceback.format_exc())
+ sys.exit(1)
+
+
+def has_get_option(config, section, option):
+ if section in config and option in config[section]:
+ return config[section][option]
+ elif section in config and option.replace('_', '-') in config[section]:
+ return config[section][option.replace('_', '-')]
+ else:
+ return False
+
+
+def split_multiline(value):
+ """Special behaviour when we have a multi line options"""
+
+ value = [element for element in
+ (line.strip() for line in value.split('\n'))
+ if element]
+ return value
+
+
+def split_csv(value):
+ """Special behaviour when we have a comma separated options"""
+
+ value = [element for element in
+ (chunk.strip() for chunk in value.split(','))
+ if element]
+ return value
+
+
+def monkeypatch_method(cls):
+ """A function decorator to monkey-patch a method of the same name on the
+ given class.
+ """
+
+ def wrapper(func):
+ orig = getattr(cls, func.__name__, None)
+ if orig and not hasattr(orig, '_orig'): # Already patched
+ setattr(func, '_orig', orig)
+ setattr(cls, func.__name__, func)
+ return func
+
+ return wrapper
+
+
+# The following classes are used to hack Distribution.command_options a bit
+class DefaultGetDict(defaultdict):
+ """Like defaultdict, but the get() method also sets and returns the default
+ value.
+ """
+
+ def get(self, key, default=None):
+ if default is None:
+ default = self.default_factory()
+ return super(DefaultGetDict, self).setdefault(key, default)
+
+
+class IgnoreDict(dict):
+ """A dictionary that ignores any insertions in which the key is a string
+ matching any string in `ignore`. The ignore list can also contain wildcard
+ patterns using '*'.
+ """
+
+ def __init__(self, ignore):
+ self.__ignore = re.compile(r'(%s)' % ('|'.join(
+ [pat.replace('*', '.*')
+ for pat in ignore])))
+
+ def __setitem__(self, key, val):
+ if self.__ignore.match(key):
+ return
+ super(IgnoreDict, self).__setitem__(key, val)
diff --git a/pbr/packaging.py b/pbr/packaging.py
index a11ff36..197a721 100644
--- a/pbr/packaging.py
+++ b/pbr/packaging.py
@@ -26,7 +26,6 @@ import re
import subprocess
import sys
-from d2to1.extern import six
from distutils.command import install as du_install
import distutils.errors
from distutils import log
@@ -34,6 +33,11 @@ import pkg_resources
from setuptools.command import install
from setuptools.command import sdist
+try:
+ import cStringIO as io
+except ImportError:
+ import io
+
log.set_verbosity(log.INFO)
TRUE_VALUES = ('true', '1', 'yes')
REQUIREMENTS_FILES = ('requirements.txt', 'tools/pip-requires')
@@ -88,16 +92,20 @@ def _missing_requires(requires):
def _pip_install(links, requires, root=None):
+ if str(os.getenv('SKIP_PIP_INSTALL', '')).lower() in TRUE_VALUES:
+ return
root_cmd = ""
if root:
root_cmd = "--root=%s" % root
- _run_shell_command(
- "%s -m pip.__init__ install %s %s %s" % (
- sys.executable,
- root_cmd,
- " ".join(links),
- " ".join(_wrap_in_quotes(requires))),
- throw_on_error=True, buffer=False)
+ missing_requires = _missing_requires(requires)
+ if missing_requires:
+ _run_shell_command(
+ "%s -m pip.__init__ install %s %s %s" % (
+ sys.executable,
+ root_cmd,
+ " ".join(links),
+ " ".join(_wrap_in_quotes(missing_requires))),
+ throw_on_error=True, buffer=False)
def read_git_mailmap(git_dir, mailmap='.mailmap'):
@@ -111,7 +119,7 @@ def canonicalize_emails(changelog, mapping):
"""Takes in a string and an email alias mapping and replaces all
instances of the aliases in the string with their real email.
"""
- for alias, email_address in six.iteritems(mapping):
+ for alias, email_address in mapping.items():
changelog = changelog.replace(alias, email_address)
return changelog
@@ -260,7 +268,7 @@ def generate_authors(git_dir=None, dest_dir='.', option_dict=dict()):
if not should_skip:
old_authors = os.path.join(dest_dir, 'AUTHORS.in')
new_authors = os.path.join(dest_dir, 'AUTHORS')
- # If there's already a ChangeLog and it's not writable, just use it
+ # If there's already an AUTHORS file and it's not writable, just use it
if (os.path.exists(new_authors)
and not os.access(new_authors, os.W_OK)):
return
@@ -285,11 +293,11 @@ def generate_authors(git_dir=None, dest_dir='.', option_dict=dict()):
mailmap = read_git_mailmap(git_dir)
with open(new_authors, 'wb') as new_authors_fh:
- new_authors_fh.write(canonicalize_emails(
- changelog, mailmap).encode('utf-8'))
if os.path.exists(old_authors):
with open(old_authors, "rb") as old_authors_fh:
- new_authors_fh.write(b'\n' + old_authors_fh.read())
+ new_authors_fh.write(old_authors_fh.read())
+ new_authors_fh.write(canonicalize_emails(
+ changelog, mailmap).encode('utf-8'))
_rst_template = """%(heading)s
@@ -483,7 +491,7 @@ try:
def _sphinx_run(self):
if not self.verbose:
- status_stream = six.StringIO()
+ status_stream = io.StringIO()
else:
status_stream = sys.stdout
confoverrides = {}
diff --git a/pbr/testr_command.py b/pbr/testr_command.py
index 0e6f247..23699dc 100644
--- a/pbr/testr_command.py
+++ b/pbr/testr_command.py
@@ -1,5 +1,18 @@
-#
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
# Copyright (c) 2013 Testrepository Contributors
#
# Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
diff --git a/pbr/tests/test_setup.py b/pbr/tests/test_setup.py
index 753b41d..21c28f3 100644
--- a/pbr/tests/test_setup.py
+++ b/pbr/tests/test_setup.py
@@ -22,7 +22,13 @@ import os
import sys
import tempfile
-from d2to1.extern import six
+try:
+ import cStringIO as io
+ BytesIO = io.StringIO
+except ImportError:
+ import io
+ BytesIO = io.BytesIO
+
import fixtures
import testscenarios
@@ -165,8 +171,8 @@ class GitLogsTest(tests.BaseTestCase):
"os.path.exists",
lambda path: os.path.abspath(path) in exist_files))
self.useFixture(fixtures.FakePopen(lambda _: {
- "stdout": six.BytesIO("Author: Foo Bar "
- "<email@bar.com>\n".encode('utf-8'))
+ "stdout": BytesIO("Author: Foo Bar "
+ "<email@bar.com>\n".encode('utf-8'))
}))
def _fake_read_git_mailmap(*args):
@@ -207,7 +213,7 @@ class GitLogsTest(tests.BaseTestCase):
lambda path: os.path.abspath(path) in exist_files))
self.useFixture(fixtures.FakePopen(lambda proc_args: {
- "stdout": six.BytesIO(
+ "stdout": BytesIO(
self._fake_log_output(proc_args["args"][2], cmd_map))
}))
diff --git a/pbr/tests/test_version.py b/pbr/tests/test_version.py
index bd72a2d..7ef908b 100644
--- a/pbr/tests/test_version.py
+++ b/pbr/tests/test_version.py
@@ -28,4 +28,4 @@ class DeferredVersionTestCase(tests.BaseTestCase):
deferred_string = MyVersionInfo("openstack").\
cached_version_string()
- self.assertEquals("5.5.5.5", deferred_string)
+ self.assertEqual("5.5.5.5", deferred_string)
diff --git a/requirements.txt b/requirements.txt
index 23525ab..160b352 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1 @@
-d2to1>=0.2.10,<0.3
setuptools_git>=0.4
diff --git a/setup.cfg b/setup.cfg
index 80dcea4..87e820b 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -6,6 +6,7 @@ summary = Python Build Reasonableness
description-file =
README.rst
home-page = http://pypi.python.org/pypi/pbr
+requires-python = >=2.6
classifier =
Development Status :: 4 - Beta
Environment :: Console
@@ -26,3 +27,7 @@ setup-hooks =
[pbr]
warnerrors = True
+
+[entry_points]
+distutils.setup_keywords =
+ pbr = pbr.core:pbr
diff --git a/setup.py b/setup.py
index dcc3d3e..c6c202a 100755
--- a/setup.py
+++ b/setup.py
@@ -16,6 +16,7 @@
import setuptools
+from pbr.d2to1 import util
+
setuptools.setup(
- setup_requires=['d2to1>=0.2.10,<0.3'],
- d2to1=True)
+ **util.cfg_to_args())
diff --git a/tox.ini b/tox.ini
index 6d32c86..97d7a78 100644
--- a/tox.ini
+++ b/tox.ini
@@ -9,7 +9,7 @@ setenv = VIRTUAL_ENV={envdir}
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands =
- python setup.py testr --slowest --testr-args='{posargs}'
+ python setup.py testr --testr-args='{posargs}'
[tox:jenkins]
sitepackages = True