Add support for virtualenv 20.x

virtualenv is undergoing a rewrite and has changed how the programmatic
API works [1]. Switch to the "new way".

While we're here, we also need to get Python 2.7 tests passing again.
That requires dropping support for upper-constraints and using our own,
limited local constraints based on supported Python versions. We also
need to migrate integration tests since those run with Python 3 now.
Update the scenarios for pip/setuptools in integration testing
similarly. Finally clean up the installation of all devstack repos as
requirements are managed different now. Instead of worrying about
syncing them we use constraints.

[1] https://github.com/pypa/virtualenv/issues/1585#issuecomment-585228492

Change-Id: I493e88985d2c4d09612fea4d20d8ffa20043a0cb
Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
Depends-On: https://review.opendev.org/739014
This commit is contained in:
Stephen Finucane 2020-04-14 14:46:21 +01:00 committed by Clark Boylan
parent 9257b68435
commit 73c11c6267
7 changed files with 78 additions and 53 deletions

View File

@ -22,7 +22,7 @@ pytz==2013.6
PyYAML==3.12 PyYAML==3.12
reno==2.5.0 reno==2.5.0
requests==2.14.2 requests==2.14.2
six==1.10.0 six==1.12.0
snowballstemmer==1.2.1 snowballstemmer==1.2.1
Sphinx==1.6.5 Sphinx==1.6.5
sphinxcontrib-apidoc==0.2.0 sphinxcontrib-apidoc==0.2.0
@ -34,4 +34,4 @@ testscenarios==0.4
testtools==2.2.0 testtools==2.2.0
traceback2==1.4.0 traceback2==1.4.0
unittest2==1.1.0 unittest2==1.1.0
virtualenv==14.0.6 virtualenv==20.0.3

View File

@ -40,6 +40,7 @@
import glob import glob
import os import os
import sys
import tarfile import tarfile
import fixtures import fixtures
@ -113,6 +114,12 @@ class TestCore(base.BaseTestCase):
def test_console_script_develop(self): def test_console_script_develop(self):
"""Test that we develop a non-pkg-resources console script.""" """Test that we develop a non-pkg-resources console script."""
if sys.version_info < (3, 0):
self.skipTest(
'Fails with recent virtualenv due to '
'https://github.com/pypa/virtualenv/issues/1638'
)
if os.name == 'nt': if os.name == 'nt':
self.skipTest('Windows support is passthrough') self.skipTest('Windows support is passthrough')

View File

@ -11,7 +11,12 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
try:
import configparser
except ImportError:
import ConfigParser as configparser
import os.path import os.path
import pkg_resources
import shlex import shlex
import sys import sys
@ -77,19 +82,35 @@ class TestIntegration(base.BaseTestCase):
# We don't break these into separate tests because we'd need separate # We don't break these into separate tests because we'd need separate
# source dirs to isolate from side effects of running pip, and the # source dirs to isolate from side effects of running pip, and the
# overheads of setup would start to beat the benefits of parallelism. # overheads of setup would start to beat the benefits of parallelism.
self.useFixture(base.CapturedSubprocess( path = os.path.join(REPODIR, self.short_name)
'sync-req', setup_cfg = os.path.join(path, 'setup.cfg')
['python', 'update.py', os.path.join(REPODIR, self.short_name)], project_name = pkg_resources.safe_name(self.short_name).lower()
cwd=os.path.join(REPODIR, 'requirements'))) # These projects should all have setup.cfg files but we'll be careful
self.useFixture(base.CapturedSubprocess( if os.path.exists(setup_cfg):
'commit-requirements', config = configparser.ConfigParser()
'git diff --quiet || git commit -amrequirements', config.read(setup_cfg)
cwd=os.path.join(REPODIR, self.short_name), shell=True)) if config.has_section('metadata'):
path = os.path.join( raw_name = config.get('metadata', 'name',
self.useFixture(fixtures.TempDir()).path, 'project') fallback='notapackagename')
self.useFixture(base.CapturedSubprocess( # Technically we should really only need to use the raw
'clone', # name because all our projects should be good and use
['git', 'clone', os.path.join(REPODIR, self.short_name), path])) # normalized names but they don't...
project_name = pkg_resources.safe_name(raw_name).lower()
constraints = os.path.join(REPODIR, 'requirements',
'upper-constraints.txt')
tmp_constraints = os.path.join(
self.useFixture(fixtures.TempDir()).path,
'upper-constraints.txt')
# We need to filter out the package we are installing to avoid
# conflicts with the constraints.
with open(constraints, 'r') as src:
with open(tmp_constraints, 'w') as dest:
for line in src:
constraint = line.split('===')[0]
if project_name != constraint:
dest.write(line)
pip_cmd = PIP_CMD + ['-c', tmp_constraints]
venv = self.useFixture( venv = self.useFixture(
test_packaging.Venv('sdist', test_packaging.Venv('sdist',
modules=['pip', 'wheel', PBRVERSION], modules=['pip', 'wheel', PBRVERSION],
@ -105,7 +126,7 @@ class TestIntegration(base.BaseTestCase):
filename = os.path.join( filename = os.path.join(
path, 'dist', os.listdir(os.path.join(path, 'dist'))[0]) path, 'dist', os.listdir(os.path.join(path, 'dist'))[0])
self.useFixture(base.CapturedSubprocess( self.useFixture(base.CapturedSubprocess(
'tarball', [python] + PIP_CMD + [filename])) 'tarball', [python] + pip_cmd + [filename]))
venv = self.useFixture( venv = self.useFixture(
test_packaging.Venv('install-git', test_packaging.Venv('install-git',
modules=['pip', 'wheel', PBRVERSION], modules=['pip', 'wheel', PBRVERSION],
@ -113,7 +134,7 @@ class TestIntegration(base.BaseTestCase):
root = venv.path root = venv.path
python = venv.python python = venv.python
self.useFixture(base.CapturedSubprocess( self.useFixture(base.CapturedSubprocess(
'install-git', [python] + PIP_CMD + ['git+file://' + path])) 'install-git', [python] + pip_cmd + ['git+file://' + path]))
if self.short_name == 'nova': if self.short_name == 'nova':
found = False found = False
for _, _, filenames in os.walk(root): for _, _, filenames in os.walk(root):
@ -127,7 +148,7 @@ class TestIntegration(base.BaseTestCase):
root = venv.path root = venv.path
python = venv.python python = venv.python
self.useFixture(base.CapturedSubprocess( self.useFixture(base.CapturedSubprocess(
'install-e', [python] + PIP_CMD + ['-e', path])) 'install-e', [python] + pip_cmd + ['-e', path]))
class TestInstallWithoutPbr(base.BaseTestCase): class TestInstallWithoutPbr(base.BaseTestCase):
@ -188,12 +209,16 @@ class TestInstallWithoutPbr(base.BaseTestCase):
class TestMarkersPip(base.BaseTestCase): class TestMarkersPip(base.BaseTestCase):
scenarios = [ scenarios = [
('pip-1.5', {'modules': ['pip>=1.5,<1.6']}),
('pip-6.0', {'modules': ['pip>=6.0,<6.1']}),
('pip-latest', {'modules': ['pip']}), ('pip-latest', {'modules': ['pip']}),
('setuptools-EL7', {'modules': ['pip==1.4.1', 'setuptools==0.9.8']}), ('setuptools-Bionic', {
('setuptools-Trusty', {'modules': ['pip==1.5', 'setuptools==2.2']}), 'modules': ['pip==9.0.1', 'setuptools==39.0.1']}),
('setuptools-minimum', {'modules': ['pip==1.5', 'setuptools==0.7.2']}), ('setuptools-Stretch', {
'modules': ['pip==9.0.1', 'setuptools==33.1.1']}),
('setuptools-EL8', {'modules': ['pip==9.0.3', 'setuptools==39.2.0']}),
('setuptools-Buster', {
'modules': ['pip==18.1', 'setuptools==40.8.0']}),
('setuptools-Focal', {
'modules': ['pip==20.0.2', 'setuptools==45.2.0']}),
] ]
@testtools.skipUnless( @testtools.skipUnless(
@ -240,25 +265,17 @@ class TestLTSSupport(base.BaseTestCase):
# These versions come from the versions installed from the 'virtualenv' # These versions come from the versions installed from the 'virtualenv'
# command from the 'python-virtualenv' package. # command from the 'python-virtualenv' package.
scenarios = [ scenarios = [
('EL7', {'modules': ['pip==1.4.1', 'setuptools==0.9.8'], ('Bionic', {'modules': ['pip==9.0.1', 'setuptools==39.0.1']}),
'py3support': True}), # And EPEL6 ('Stretch', {'modules': ['pip==9.0.1', 'setuptools==33.1.1']}),
('Trusty', {'modules': ['pip==1.5', 'setuptools==2.2'], ('EL8', {'modules': ['pip==9.0.3', 'setuptools==39.2.0']}),
'py3support': True}), ('Buster', {'modules': ['pip==18.1', 'setuptools==40.8.0']}),
('Jessie', {'modules': ['pip==1.5.6', 'setuptools==5.5.1'], ('Focal', {'modules': ['pip==20.0.2', 'setuptools==45.2.0']}),
'py3support': True}),
# Wheezy has pip1.1, which cannot be called with '-m pip'
# So we'll use a different version of pip here.
('WheezyPrecise', {'modules': ['pip==1.4.1', 'setuptools==0.6c11'],
'py3support': False})
] ]
@testtools.skipUnless( @testtools.skipUnless(
os.environ.get('PBR_INTEGRATION', None) == '1', os.environ.get('PBR_INTEGRATION', None) == '1',
'integration tests not enabled') 'integration tests not enabled')
def test_lts_venv_default_versions(self): def test_lts_venv_default_versions(self):
if (sys.version_info[0] == 3 and not self.py3support):
self.skipTest('This combination will not install with py3, '
'skipping test')
venv = self.useFixture( venv = self.useFixture(
test_packaging.Venv('setuptools', modules=self.modules)) test_packaging.Venv('setuptools', modules=self.modules))
bin_python = venv.python bin_python = venv.python

View File

@ -183,7 +183,8 @@ class Venv(fixtures.Fixture):
def _setUp(self): def _setUp(self):
path = self.useFixture(fixtures.TempDir()).path path = self.useFixture(fixtures.TempDir()).path
virtualenv.create_environment(path, clear=True) virtualenv.cli_run([path])
python = os.path.join(path, 'bin', 'python') python = os.path.join(path, 'bin', 'python')
command = [python] + self.pip_cmd + ['-U'] command = [python] + self.pip_cmd + ['-U']
if self.modules and len(self.modules) > 0: if self.modules and len(self.modules) > 0:

View File

@ -6,12 +6,13 @@ wheel>=0.32.0 # MIT
fixtures>=3.0.0 # Apache-2.0/BSD fixtures>=3.0.0 # Apache-2.0/BSD
hacking>=1.1.0,<1.2.0 # Apache-2.0 hacking>=1.1.0,<1.2.0 # Apache-2.0
mock>=2.0.0 # BSD mock>=2.0.0 # BSD
six>=1.10.0 # MIT six>=1.12.0 # MIT
stestr>=2.1.0 # Apache-2.0 stestr>=2.1.0,<3.0;python_version=='2.7' # Apache-2.0
stestr>=2.1.0;python_version>='3.0' # Apache-2.0
testresources>=2.0.0 # Apache-2.0/BSD testresources>=2.0.0 # Apache-2.0/BSD
testscenarios>=0.4 # Apache-2.0/BSD testscenarios>=0.4 # Apache-2.0/BSD
testtools>=2.2.0 # MIT testtools>=2.2.0 # MIT
virtualenv>=14.0.6 # MIT virtualenv>=20.0.3 # MIT
coverage!=4.4,>=4.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0
# optionally exposed by distutils commands # optionally exposed by distutils commands

View File

@ -97,10 +97,6 @@ name = test_project
[entry_points] [entry_points]
console_scripts = console_scripts =
test_cmd = test_project:main test_cmd = test_project:main
[global]
setup-hooks =
pbr.hooks.setup_hook
EOF EOF
cat <<EOF > setup.py cat <<EOF > setup.py
@ -115,18 +111,19 @@ from socket import error as SocketError
try: try:
setuptools.setup( setuptools.setup(
setup_requires=['pbr'], setup_requires=['pbr'],
pbr=True) pbr=True,
)
except (SocketError, Timeout): except (SocketError, Timeout):
setuptools.setup( setuptools.setup(
setup_requires=['pbr'], setup_requires=['pbr'],
pbr=True) pbr=True,
)
EOF EOF
mkdir test_project mkdir test_project
cat <<EOF > test_project/__init__.py cat <<EOF > test_project/__init__.py
def main(): def main():
print "Test cmd" print("Test cmd")
EOF EOF
epvenv=$eptest/venv epvenv=$eptest/venv
@ -165,6 +162,8 @@ export REPODIR
export WHEELHOUSE export WHEELHOUSE
export OS_TEST_TIMEOUT=600 export OS_TEST_TIMEOUT=600
cd $REPODIR/pbr cd $REPODIR/pbr
tox -epy27 --notest mkvenv .venv
.tox/py27/bin/python -m pip install ${REPODIR}/requirements source .venv/bin/activate
tox -epy27 -- test_integration pip install -r test-requirements.txt
pip install ${REPODIR}/requirements
stestr run --suppress-attachments test_integration

View File

@ -11,8 +11,9 @@ setenv =
OS_STDOUT_CAPTURE={env:OS_STDOUT_CAPTURE:1} OS_STDOUT_CAPTURE={env:OS_STDOUT_CAPTURE:1}
OS_STDERR_CAPTURE={env:OS_STDERR_CAPTURE:1} OS_STDERR_CAPTURE={env:OS_STDERR_CAPTURE:1}
OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:60} OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:60}
# NOTE(stephenfin): pbr intentionally does not use constraints since we support
# a broader range of Python versions than OpenStack as a whole
deps = deps =
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/test-requirements.txt -r{toxinidir}/test-requirements.txt
commands = stestr run --suppress-attachments {posargs} commands = stestr run --suppress-attachments {posargs}
@ -22,7 +23,6 @@ commands = flake8 {posargs}
[testenv:docs] [testenv:docs]
whitelist_externals = rm whitelist_externals = rm
deps = deps =
-c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/doc/requirements.txt -r{toxinidir}/doc/requirements.txt
commands = commands =
rm -rf doc/build doc/source/reference/api rm -rf doc/build doc/source/reference/api